给出一个 N * M 的矩阵,每个格放着一个非负数,要求选出一些数,使他们的和最大,要求是有相邻边的格子里的数不能同时选。
先说,我压根没想过这事网络流……因为方格取数(1)是个状态压缩……
看了题解,才明白的:
这个题由于数据范围较大,所以状态压缩过不去,需要用网络流,我重复一遍人家的建图:
我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有:
最大点权独立集 + 最小点权覆盖集 = 总点权和,
这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图,
1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一条边,权值为该黑色点的权值,
2,从白色的点向汇点连一条边,权值为该白色点的权值,
3,然后,对于每一对相邻的黑白点,从黑点向白点连一条边,权值为无穷大。
最后求最小割(最大流),即为最小点权覆盖集。
因为我们求出的最小割集一定是从那些相邻的黑白点之间的边(也就是不能用的边,因为相邻的数不能同时选)中选出来的,且是最小代价,也就是说从方格中拿掉的数之和尽量小,那么剩下的数之和一定是最大的。
我只能说,神奇的网络流!!!!Orz!!!!
代码:
#include<cstdio> #include<cstring> #include<queue> #include<cmath> #define find_min(a,b) a<b?a:b using 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; }