1. 程式人生 > >[Luogu] U18202 洞穴遇險

[Luogu] U18202 洞穴遇險

bool ref 什麽 暴力 lang ret etc || gpo

https://www.luogu.org/problemnew/show/U18202

暴力搜索預期得分3030分左右。

狀壓預期得分7070分左右。

考慮費用流,將剩余不穩定度和最小轉為消除不穩定度和最大。

首先拐角處只能放在有不穩定度的格子上:如果它的拐角處放在了X+YX+Y為偶數的格子上,那麽它不僅沒有減少不穩定度,而且還占地。這個貪心顯然正確。

那麽黑白染色,X+YX+Y為偶數的點分一類,為奇數的分一類。將LL形柱子抽象成兩個非拐角處的格子的路徑(這兩個格子的X+YX+Y都為偶數,拐角處的第三個格子的X+YX+Y為奇數)。那麽這兩個格子肯定一個在奇數列一個在偶數列,證明顯然。

於是建圖:

  • 一共有四列點。

  • X+Y為偶數的點再分為兩類:奇數列的和偶數列的。

  • 奇數列的點放在左邊(第一列),源點向每個點連一條容量為1,費用為0的邊。

  • 偶數列的點放在右邊(第四列),每個點向匯點連一條容量為1,費用為0的邊。

  • X+Y為奇數的點每個點拆開,一分為二,分別放在第二列和第三列。第二列的每個點向第三列的自己連一條容量為1,費用為負的該點的權值(不穩定度)。

  • 如果第一列(X+Y為偶數,奇數列的點)和第二列的點相鄰,連一條容量為1,費用為0的邊,第三、四列同理。

  • 當然塌了的格子不能連邊。

跑一遍最小費用最大流即可。這樣建圖每次增廣的流都肯定為11,所以可以在最大流為MM的時候直接跳出。期望得分4040分。

(???)

因為這樣是錯的,

4 4 6
0 1 0 1000
0 0 0 0
0 1 0 1
0 0 0 0
1 1
2 1
2 3
4 1
4 3
4 4

就是一個構造的反例。我們不能先保證總流量最大再保證總費用最小,而是要盡可能地讓總費用最小。也就是說我們要把柱子用在刀刃上,而不是放越多越好。於是想到如果本次增廣源點到匯點的費用為正,直接跳出即可。(我的構造方式是連負費用邊,如果連正權邊跑最長路那麽費用為負跳出即可)

彩蛋:

額,我不太會構造這樣的數據,就把這一小片乘以1000貼在後面每個數據的左上角了。所以這麽寫也可以AC:printf("%d\n",sum+mincost-998000);

額我在說什麽...

這樣就可以100100分,復雜度O(O(費用流))。

題解:https://www.luogu.org/blog/zhoutb2333/dong-xue-yu-xian-ti-xie

暴力:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <ctime>
#include <cstdlib> 

using namespace std;
const int N = 65;

#define yxy getchar()
#define R freopen("gg.in", "r", stdin)

bool vis[N][N];
int w[N][N];
int n, m, k, Answer, All;

inline int read(){
    int x = 0; char c = yxy;
    while(c < 0 || c > 9) c = yxy;
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = yxy;
    return x;
}

void dfs(int step, int cut, int I, int J){
    if(cut > Answer) Answer = cut;
    if(step == m + 1) return; 
    for(int i = 1; i <= n; i ++) {
        for(int j = 1; j <= n; j ++){
            if(i < I && j < J) continue ;
            if(!vis[i][j] && w[i][j]){ //以當前點為拐點,每個點處枚舉四種狀態 
                int x, y, x_, y_;
                x = i, y = j - 1, x_ = i - 1, y_ = j; 
                if(y > 0 && x_ > 0 && !vis[x][y] && !vis[x_][y_]) { //朝向左上 
                    vis[i][j] = 1; vis[x][y] = 1; vis[x_][y_] = 1;
                    cut += w[i][j];
                    dfs(step + 1, cut, i, j);
                    cut -= w[i][j];
                    vis[i][j] = 0; vis[x][y] = 0; vis[x_][y_] = 0;
                }
                x = i + 1, y = j, x_ = i, y_ = j + 1;
                if(x <= n && y_ <= n && !vis[x][y] && !vis[x_][y_]) { //朝向右下
                    vis[i][j] = 1; vis[x][y] = 1; vis[x_][y_] = 1;
                    cut += w[i][j];
                    dfs(step + 1, cut, i, j);
                    cut -= w[i][j];
                    vis[i][j] = 0; vis[x][y] = 0; vis[x_][y_] = 0;
                }
                x = i, y = j - 1, x_ = i + 1, y_ = j;
                if(y > 0 && x_ <= n && !vis[x][y] && !vis[x_][y_]) { // 左下 
                    vis[i][j] = 1; vis[x][y] = 1; vis[x_][y_] = 1;
                    cut += w[i][j];
                    dfs(step + 1, cut, i, j);
                    cut -= w[i][j];
                    vis[i][j] = 0; vis[x][y] = 0; vis[x_][y_] = 0;
                }
                x = i - 1, y = j, x_ = i, y_ = j + 1;
                if(x > 0 && y_ <= n && !vis[x][y] && !vis[x_][y_]) { // 右上 
                    vis[i][j] = 1; vis[x][y] = 1; vis[x_][y_] = 1;
                    cut += w[i][j];
                    dfs(step + 1, cut, i, j);
                    cut -= w[i][j];
                    vis[i][j] = 0; vis[x][y] = 0; vis[x_][y_] = 0;
                }
            }
        }
    }
}

int main()
{    
    n = read(); m = read(); k = read();
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++){
            int W = read();
            w[i][j] = W, All += w[i][j];
        }
            
    for(int i = 1; i <= k; i ++){
        int x = read(), y = read();
        vis[x][y] = 1; //標記為1的已經塌陷 
    }
    dfs(1, 0, 1, 1);
    printf("%d", All - Answer);
    return 0;
}

正解

#include<bits/stdc++.h>
#define maxn 105
#define maxe 100010
#define INF 1<<30
using namespace std;

int head[maxe],nxt[maxe],point[maxe],flow[maxe],val[maxe],tot=1;
int pre[maxe],preedge[maxe],tmpflow[maxe],dis[maxe],s=0,t=100000,E,ofs=25000;
int v[maxn][maxn],sum,n,m,k;
bool in[maxe];
queue<int> q;
int get(int x,int y,int lvl){
    return (x-1)*n+y+ofs*lvl;
}
bool chk(int num){
    num%=ofs;
    int x=(num-1)/n+1,y=(num-1)%n+1;
    if(x<1||y<1||x>n||y>n||v[x][y]==-1)
        return false;
    return true; 
}
void ADD(int x,int y,int f,int c){
    val[++tot]=c;
    flow[tot]=f;
    point[tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void add(int x,int y,int f,int c){
    if((x!=s&&x!=t&&!chk(x))||(y!=s&&y!=t&&!chk(y)))
        return;
    ADD(x,y,f,c),ADD(y,x,0,-c);
}
bool bfs(){
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0,in[s]=true,q.push(s),tmpflow[s]=m;
    while(!q.empty()){
        int x=q.front();
        in[x]=false;
        q.pop();
        for(int j=head[x];j;j=nxt[j]){
            if(!flow[j]||dis[point[j]]<=dis[x]+val[j])
                continue;
            dis[point[j]]=dis[x]+val[j];
            pre[point[j]]=x;
            preedge[point[j]]=j;
            tmpflow[point[j]]=min(tmpflow[x],flow[j]);
            if(!in[point[j]])
                in[point[j]]=true,q.push(point[j]);
        }
    }
    return dis[t]!=0x3f3f3f3f;
}
int main(){
    int x,y,mincost=0;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&v[i][j]),sum+=v[i][j];
    for(int i=1;i<=k;i++)
        scanf("%d%d",&x,&y),v[x][y]=-1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if((i+j)%2)
                add(get(i,j,1),get(i,j,2),1,-v[i][j]);
            else{
                if(j%2){
                    add(s,get(i,j,0),1,0);
                    add(get(i,j,0),get(i,j-1,1),1,0);
                    add(get(i,j,0),get(i,j+1,1),1,0);
                    add(get(i,j,0),get(i-1,j,1),1,0);
                    add(get(i,j,0),get(i+1,j,1),1,0);
                }
                else{
                    add(get(i,j,3),t,1,0);
                    add(get(i,j-1,2),get(i,j,3),1,0);
                    add(get(i,j+1,2),get(i,j,3),1,0);
                    add(get(i-1,j,2),get(i,j,3),1,0);
                    add(get(i+1,j,2),get(i,j,3),1,0);
                }
            }
        }
    while(bfs()&&m){
        if(dis[t]>0)
            break;
        int k=t;
        while(k!=s){
            flow[preedge[k]]-=tmpflow[t];
            flow[preedge[k]^1]+=tmpflow[t];
            k=pre[k];
        }
        m-=tmpflow[t];
        mincost+=dis[t]*tmpflow[t];
    }
    printf("%d\n",sum+mincost);
    return 0; 
} 

[Luogu] U18202 洞穴遇險