[BZOJ3171/Luogu3965][TJOI2013]迴圈格
阿新 • • 發佈:2019-01-02
題目連結:
首先,有一個很顯然的結論,每一個格點都要有且只有一條入邊,因為整張圖有\(r*c\)個點和邊,每個點都要有入邊。
現在要平均分配每一條邊,次數最小,那麼就很簡單了,費用流。
把每個點拆成入點和出點,對於每個入點,和源點連邊,容量\(1\),費用\(0\),表示可以向其他點貢獻入邊。
對於每個點向四周連邊,容量為\(1\)(可以向四周連邊),若方向與箭頭相同費用為\(0\)(不需要改變),否則為\(1\)(需要修改)。
對於每個出點向匯點連邊,容量\(1\),費用\(0\)(得到了一條入邊)。
#include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define ID(x,y,z) ((z)*n*m+((x)-1)*m+(y)) inline int Min(int a,int b){return a<b?a:b;} inline int Max(int a,int b){return a>b?a:b;} int n,m,St,Ed,MaxFlow,MinCost; int Head[505],Next[6005],To[6005],Val[6005],Cos[6005],En=1; int Pre[505],Ref[505],Dis[505]; char Grid[20]; bool Inq[505]; const int nx[]={-1,1,0,0},ny[]={0,0,-1,1}; const char s[]={'U','D','L','R'}; inline void Add(int x,int y,int z,int w) { Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z,Cos[En]=+w; Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0,Cos[En]=-w; } bool SPFA()//壓行費用流,不要在意 { std::queue<int> q; memset(Dis,0x3f,sizeof Dis); q.push(St),Dis[St]=0,Ref[St]=1<<30; for(int x,y;!q.empty();q.pop(),Inq[x]=false) for(int i=Head[x=q.front()];i;i=Next[i]) if(Val[i]&&Dis[y=To[i]]>Dis[x]+Cos[i]) { Dis[y]=Dis[x]+Cos[Pre[y]=i]; Ref[y]=Min(Ref[x],Val[i]); if(!Inq[y])q.push(y),Inq[y]=true; } if(Dis[Ed]==0x3f3f3f3f)return false; MaxFlow+=Ref[Ed],MinCost+=Ref[Ed]*Dis[Ed]; for(int x=Ed,i;x!=St;x=To[i^1]) Val[i=Pre[x]]-=Ref[Ed],Val[i^1]+=Ref[Ed]; return true; } int main() { scanf("%d%d",&n,&m),St=n*m<<1|1,Ed=St+1; for(int i=1;i<=n;++i) { scanf("%s",Grid+1); for(int j=1;j<=m;++j) { int t=std::find(s,s+4,Grid[j])-s; Add(St,ID(i,j,0),1,0); Add(ID(i,j,1),Ed,1,0);//向源點與匯點連邊 for(int k=0;k<4;++k) { int wx=i+nx[k],wy=j+ny[k];//箭頭方向 if(!wx)wx=n; else if(wx>n)wx=1; if(!wy)wy=m; else if(wy>m)wy=1;//邊界處理 Add(ID(i,j,0),ID(wx,wy,1),1,k!=t); } } } while(SPFA()); printf("%d\n",MinCost); return 0; }