[CQOI2012]交換棋子,洛谷P3159,最小費用最大流
阿新 • • 發佈:2018-12-04
正題
兩個很顯然的性質:
1.我們只會交換兩個顏色不同的棋子。
2.如果路上有棋子,那麼這條路肯定沒有前面的棋子走優。
那麼,就相當於黑棋經過一堆白棋走到一個白棋。
我們來考慮交換問題。
除了一條交換路徑的頭尾交換一次之外,每個點都要被交換兩次。
怎麼解決?
解法十分顯然,拆三個點,a向b連邊,b向c連邊我們規定從起點從b出發,終點在b結束,那麼消耗的流量就是所消耗的交換次數。
a,b,c連邊的流量怎麼設定呢?
很容易想到,兩條邊各取。
我們再來考慮頭尾的交換。
那些一開始為黑,最後為白的點肯定要把這個點釋放出去,那麼我們先優先給出邊bc流量加1。
那些一開始為白,最後為黑的點肯定要獲得一個黑點,那麼我們先優先給入邊ac流量加1.
剩下的除以2均攤就可以了,因為一個黑點只會經過這裡,不會停在這裡,除以2帶來的價值是最大的。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<queue> using namespace std; int n,m; char st[25][25],ed[25][25],p[25][25]; struct edge{ int x,y,next,c,cos; }s[50010]; int begin,end,len=1; int fx[8]={-1,-1,-1,0,0,1,1,1}; int fy[8]={-1,0,1,-1,1,-1,0,1}; int ans=0; int d[1210],last[1210],mmin[1210],first[1210]; bool tf[1210]; queue<int> f; void ins(int x,int y,int c,int cos){ len++;s[len]=(edge){x,y,first[x],c,cos};first[x]=len; len++;s[len]=(edge){y,x,first[y],0,-cos};first[y]=len; } bool SPFA(int&flow,int&cost){ mmin[begin]=1e9; memset(d,63,sizeof(d));d[begin]=0; memset(tf,false,sizeof(tf));tf[begin]=true; f.push(begin); while(!f.empty()){ int x=f.front();f.pop();tf[x]=false; for(int i=first[x];i!=0;i=s[i].next){ int y=s[i].y; if(d[y]>d[x]+s[i].cos && s[i].c>0){ d[y]=d[x]+s[i].cos; last[y]=i; mmin[y]=min(mmin[x],s[i].c); if(!tf[y]){ tf[y]=true; f.push(y); } } } } if(d[end]==d[end+1]) return false; flow+=mmin[end]; cost+=mmin[end]*d[end]; int now=end; while(now!=begin){ s[last[now]].c-=mmin[end];s[last[now]^1].c+=mmin[end]; now=s[last[now]].x; } return true; } void MCMF(){ int flow=0,cost=0; while(SPFA(flow,cost)); if(flow==ans) printf("%d\n",cost); else printf("-1"); } int main(){ scanf("%d %d",&n,&m); int data,temp,t,x,y; begin=0;end=(data=n*m)*3+1; for(int i=1;i<=n;i++) scanf("%s",st[i]+1); for(int i=1;i<=n;i++) scanf("%s",ed[i]+1); for(int i=1;i<=n;i++) scanf("%s",p[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ temp=(i-1)*m+j,t=p[i][j]-'0'; if(st[i][j]==ed[i][j]) ins(temp,temp+data,t/2,0),ins(temp+data,temp+data+data,t/2,0); else if(st[i][j]=='0'){ ins(begin,temp+data,1,0); ins(temp,temp+data,t/2,0);ins(temp+data,temp+data+data,(t+1)/2,0); } else{ ins(temp+data,end,1,0);ans++; ins(temp,temp+data,(t+1)/2,0);ins(temp+data,temp+data+data,t/2,0); } for(int k=0;k<8;k++){ x=i+fx[k],y=j+fy[k]; if(x>=1 && x<=n && y>=1 && y<=m);else continue; t=(x-1)*m+y; ins(temp+data+data,t,1e9,1); } } MCMF(); }