給出一個 N * M 的矩陣,每個格放著一個非負數,要求選出一些數,使他們的和最大,要求是有相鄰邊的格子裡的數不能同時選。
先說,我壓根沒想過這事網路流……因為方格取數(1)是個狀態壓縮……
看了題解,才明白的:
這個題由於資料範圍較大,所以狀態壓縮過不去,需要用網路流,我重複一遍人家的建圖:
我們知道對於普通二分圖來說,最大獨立點集 + 最小點覆蓋集 = 總點數,類似的,對於有權的二分圖來說,有:
最大點權獨立集 + 最小點權覆蓋集 = 總點權和,
這個題很明顯是要求 最大點權獨立集 ,現在 總點權 已知,我們只要求出來 最小點權覆蓋集 就好了,我們可以這樣建圖,
1,對矩陣中的點進行黑白著色(相鄰的點顏色不同),從源點向黑色的點連一條邊,權值為該黑色點的權值,
2,從白色的點向匯點連一條邊,權值為該白色點的權值,
3,然後,對於每一對相鄰的黑白點,從黑點向白點連一條邊,權值為無窮大。
最後求最小割(最大流),即為最小點權覆蓋集。
因為我們求出的最小割集一定是從那些相鄰的黑白點之間的邊(也就是不能用的邊,因為相鄰的數不能同時選)中選出來的,且是最小代價,也就是說從方格中拿掉的數之和盡量小,那麼剩下的數之和一定是最大的。
我只能說,神奇的網路流!!!!Orz!!!!
代碼:
#include<cstdio>#include<cstring>#include<queue>#include<cmath>#define find_min(a,b) a<b?a:busing namespace std;const int N = 2550;const int MAX = 100000;struct Edge{ int s,e,v; int next;}edge[20*N];int dir[4][2]={-1,0, 1,0, 0,-1, 0,1};int n,m,e_num,head[N],d[N],sp,tp;void AddEdge(int a,int b,int c){ edge[e_num].s=a; edge[e_num].e=b; edge[e_num].v=c; edge[e_num].next=head[a]; head[a]=e_num++; edge[e_num].s=b; edge[e_num].e=a; edge[e_num].v=0; edge[e_num].next=head[b]; head[b]=e_num++;}int judge(int i,int j,int k){ int ii=i+dir[k][0]; int jj=j+dir[k][1]; if(ii>=1 && ii<=n && jj>=1 && jj<=m)return 1; return 0;}int bfs(){queue <int> q;memset(d,-1,sizeof(d));d[sp]=0;q.push(sp);while(!q.empty()){int cur=q.front();q.pop();for(int i=head[cur];i!=-1;i=edge[i].next){int u=edge[i].e;if(d[u]==-1 && edge[i].v>0){d[u]=d[cur]+1;q.push(u);}}}return d[tp] != -1;}int dfs(int a,int b){int r=0;if(a==tp)return b;for(int i=head[a];i!=-1 && r<b;i=edge[i].next){int u=edge[i].e;if(edge[i].v>0 && d[u]==d[a]+1){int x=find_min(edge[i].v,b-r);x=dfs(u,x);r+=x;edge[i].v-=x;edge[i^1].v+=x;}}if(!r)d[a]=-2;return r;}int dinic(int sp,int tp){int total=0,t;while(bfs()){while(t=dfs(sp,MAX))total+=t;}return total;}int main(){ int i,j,k,a; while(~scanf("%d%d",&n,&m)) { int sum=0; e_num=0; memset(head,-1,sizeof(head)); sp=0; tp=n*m+1; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ scanf("%d",&a); sum+=a; int x=(i-1)*m+j; if((i+j)%2==0){ AddEdge(sp,x,a); for(k=0;k<4;k++){ if(judge(i,j,k)==1){//不出界 int y=(i+dir[k][0]-1)*m+(j+dir[k][1]); AddEdge(x,y,MAX);//這裡要注意邊的方向,是黑點向白點連邊 } } } else{ AddEdge(x,tp,a); for(k=0;k<4;k++){ if(judge(i,j,k)==1){//不出界 int y=(i+dir[k][0]-1)*m+(j+dir[k][1]); AddEdge(y,x,MAX);//注意邊的方向,和上面的是相反的 } } } } } int max_flow=dinic(sp,tp); printf("%d\n",sum-max_flow); } return 0;}