1. 程式人生 > >「清華集訓 2017」無限之環

「清華集訓 2017」無限之環

無限之WA

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

本題如果知道是網路流的話,其實建圖不算特別神奇,但是比較麻煩。

資料範圍過大,插頭dp不能處理,而且是一個網格圖,考慮網路流。

先看是不是二分圖?

每個格子只會和相鄰四個格子發生關係

所以,黑白染色正好。

i+j為偶數左部點,i+j為奇數右部點

不漏水是什麼?

每個管道的四個口都能和別的接好。

S向左,右向T連口數的容量,費用為0的邊

考慮怎麼能把左右連在一起。

考慮要上下左右四個方向匹配,那麼必然還要拆點(但是這些點之間是並列關係)。

每個本點向四個分點之間考慮連邊(在這裡考慮旋轉以及費用)

考慮通過流量流過來表示選擇旋轉與否,恰當的邊賦恰當的值。

sz=1:

自己方向是(1,0)相鄰方向(1,1),對面(1,2)

sz=2:

直線型:自己方向(1,0),另外兩個不連(因為不能轉),

非直線型:自己兩個方向(1,0),每個自己方向+2(-2),連(1,1),(手動模擬一下這樣選擇的四種流法對應四種旋轉的位置)

sz=3:

自己(1,0),剩下一個0位置,距離為1的向它連(1,1),距離為2的向它連(1,2)

sz=4

四個方向(1,0);

(注意,右邊的單向邊方向和左邊的完全相反)

 

然後左右分點之間相鄰的關係上下,左右,下上,右左,左分點連到對應的右分點。

然後跑最小費用最大流。

 

至於-1

先判斷黑格口數是不是等於白格口數

然後如果最大流不是口數(滿流)的話,就-1

(其實資料沒有-1的點23333~~~)

 

這樣,一條流的意義是什麼?左邊的某個口和右邊的某個口,通過旋轉或者不旋轉連線在了一起,同時兩個管道的需求都少了1

由於所有邊的流量都是1(除了和ST連的),所以每個口只會流出1流,減少1的需求,

如果最後滿流

那麼意味著,所有的口都滿足了自己的需求,即每個口都連上了。而且每個流都合法。

 

出錯點:

1.陣列開小了。。。。has[4],四位二進位制數,少開一位。。。(這個導致開O2之後超級厭氧,全部輸出-1WA掉,一定程度上轉移了查錯重心。。。)

2.提取四位二進位制數的時候,習慣性地寫成了while(tmp) has[++tot]=tmp%2,tmp>>=1;然鵝,最高位是0的話,沒有提取完4位就break了。而且has沒有memset,高位就存上了之前可能的1.。。。。導致WA死。。

其實開始找規律一點沒錯。。。。但是由於while高位0的鍋,以為找錯了,,,最後還打了暴力判斷。。。。

 

 

程式碼:(得開O2)

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=2000+2;
const int inf=0x3f3f3f3f;
int n,m,s,t;
struct node{
    int nxt,to;
    int w,v;
}e[(5*N+4*N+N*6)*2];
int hd[5*N],cnt=1;
void add(int x,int y,int w,int v){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].w=w;
    e[cnt].v=v;
    hd[x]=cnt;
    
    e[++cnt].nxt=hd[y];
    e[cnt].to=x;
    e[cnt].w=0;
    e[cnt].v=-v;
    hd[y]=cnt;
}
int incf[5*N],dis[5*N];
int pre[5*N];
bool vis[5*N];
queue<int>q;
bool spfa(){
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof vis);
    memset(dis,inf,sizeof dis);
    dis[s]=0;
    incf[s]=inf;
    pre[s]=0;
    q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        vis[x]=0;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(e[i].w&&dis[y]>dis[x]+e[i].v){
                dis[y]=dis[x]+e[i].v;
                pre[y]=i;
                incf[y]=min(e[i].w,incf[x]);
                if(!vis[y]){
                    vis[y]=1;
                    q.push(y);
                }
            }    
        }
    }
    if(dis[t]==inf) return false;
    return true;
}
int ans,maxflow;
void upda(){
    int x=t;
    while(pre[x]){
        e[pre[x]].w-=incf[t];
        e[pre[x]^1].w+=incf[t];
        x=e[pre[x]^1].to;
    }
    ans+=incf[t]*dis[t];
    maxflow+=incf[t];
    //cout<<" ans "<<ans<<" "<<maxflow<<endl;
}
int has[10],tot;
int mp[N][N];
int sz[N];
int num(int x,int y,int k){//5 is itself
    return ((x-1)*m+y-1)*5+k;
}
int main(){
    rd(n);rd(m);
    s=0,t=n*m*5+1;
    for(reg i=1;i<=16;++i){
        sz[i]=sz[i>>1]+(i&1);
    }
    int le=0,ri=0;
    for(reg i=1;i<=n;++i){
        for(reg j=1;j<=m;++j){
            rd(mp[i][j]);
            if((i+j)%2==0) add(s,num(i,j,5),sz[mp[i][j]],0),le+=sz[mp[i][j]];
            else add(num(i,j,5),t,sz[mp[i][j]],0),ri+=sz[mp[i][j]];
        }
    }
    if(le!=ri){
        printf("-1");return 0;
    }
    for(reg i=1;i<=n;++i){
        for(reg j=1;j<=m;++j){
            for(reg l=1;l<=4;++l){
                has[l]=(mp[i][j]>>(l-1))&1;
            }
            int now=num(i,j,5);
            tot=sz[mp[i][j]];
            if((i+j)%2==0){
                switch(tot){
                    case 0:{
                        break;
                    }
                    case 1:{
                        int pos=0;
                        //cout<<" find "<<mp[i][j]<<" :: "<<has[1]<<" "<<has[2]<<" "<<has[3]<<" "<<has[4]<<endl;
                        for(reg l=1;l<=4;++l) if(has[l]) {
                            pos=l;
                        }
                        add(now,num(i,j,pos),1,0);
                        add(now,num(i,j,pos%4+1),1,1);
                        add(now,num(i,j,pos==1?4:pos-1),1,1);
                        add(now,num(i,j,(pos+2<=4)?pos+2:pos-2),1,2);
                        break;
                    }
                    case 2:{
                        if(mp[i][j]==5||mp[i][j]==10){
//    cout<<" dkfjdf "<<endl;
                            for(reg l=1;l<=4;++l){
                                if(has[l]) add(now,num(i,j,l),1,0);
                            }
                        }else{
                            for(reg l=1;l<=4;++l){
                                if(has[l]) {
                                    add(now,num(i,j,l),1,0);
                                    add(num(i,j,l),num(i,j,(l+2<=4)?l+2:l-2),1,1);
                                }
                            }
                        }
                        break;
                    }
                    case 3:{
                        for(reg l=1;l<=4;++l){
                            if(has[l]){
                                add(now,num(i,j,l),1,0);
                            }else{
                                add(num(i,j,(l+1)<=4?l+1:l-3),num(i,j,l),1,1);
                                add(num(i,j,(l+3)<=4?l+3:l-1),num(i,j,l),1,1);
                                add(num(i,j,(l+2)<=4?l+2:l-2),num(i,j,l),1,2);
                            }
                        }
                        break;
                    }
                    case 4:{
                        for(reg l=1;l<=4;++l){
                            add(now,num(i,j,l),1,0);
                        }
                        break;
                    }
                }
            }else{
                switch(tot){
                    case 0:{
                        break;
                    }
                    case 1:{
                        int pos=0;
                        for(reg l=1;l<=4;++l) if(has[l]){
                             pos=l;
                        }
                        add(num(i,j,pos),now,1,0);
                        add(num(i,j,pos%4+1),now,1,1);
                        add(num(i,j,pos==1?4:pos-1),now,1,1);
                        add(num(i,j,(pos+2<=4)?pos+2:pos-2),now,1,2);
                        break;
                    }
                    case 2:{
                        if(mp[i][j]==5||mp[i][j]==10){
                            for(reg l=1;l<=4;++l){
                                if(has[l]) add(num(i,j,l),now,1,0);
                            }
                        }else{
                            for(reg l=1;l<=4;++l){
                                if(has[l]) {
                                    add(num(i,j,l),now,1,0);
                                    add(num(i,j,(l+2<=4)?l+2:l-2),num(i,j,l),1,1);
                                }
                            }
                        }
                        break;
                    }
                    case 3:{
                        for(reg l=1;l<=4;++l){
                            if(has[l]){
                                add(num(i,j,l),now,1,0);
                            }else{
                                add(num(i,j,l),num(i,j,(l+1)<=4?l+1:l-3),1,1);
                                add(num(i,j,l),num(i,j,(l+3)<=4?l+3:l-1),1,1);
                                add(num(i,j,l),num(i,j,(l+2)<=4?l+2:l-2),1,2);
                            }
                        }
                        break;
                    }
                    case 4:{
                        for(reg l=1;l<=4;++l){
                            add(num(i,j,l),now,1,0);
                        }
                        break;
                    }
                }
            }
            if((i+j)%2==0){
                if(i>1) add(num(i,j,1),num(i-1,j,3),1,0);
                if(i<n)    add(num(i,j,3),num(i+1,j,1),1,0);
                if(j>1) add(num(i,j,4),num(i,j-1,2),1,0);
                if(j<m) add(num(i,j,2),num(i,j+1,4),1,0);
            } 
        }
    }
    while(spfa()) upda();
    //cout<<" maxflow "<<maxflow<<endl;
    if(maxflow!=le){
        puts("-1");return 0;
    }
    printf("%d",ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/14 21:08:15
*/

總結:

這個題的突破口的話,

發現插頭dp不行,那麼網格圖,可能就是網路流(資料範圍也支援)

黑白染色可以。那麼考慮最終合法的結果是什麼意義。然後處理好旋轉連邊。

(發現沒有,直線型為什麼不能轉?因為這樣會同時轉2個點!網路流沒辦法處理這種旋轉(除非你大力討論))