HDU1569 方格取数(2)(最大点权独立集 + 最小点权覆盖集 = 总权和)

来源:互联网 发布:php导出excel乱码问题 编辑:程序博客网 时间:2024/05/18 04:00

给出一个 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;}



 


原创粉丝点击