方格取数(2)
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3663 Accepted Submission(s): 1148
Problem Description
给你一个m*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括2整数m,n和m*n个非负数(m<=50,n<=50)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3 3
75 15 21
75 15 28
34 70 5
Sample Output
188
Author
ailyanlu
Source
Recommend
8600
思路:
最大点权独立集=总权值-最小点权覆盖集
最小割=最大流
最小点权覆盖集=最小割
根据奇偶建立二分图,
if(i+j)%2==0 源点和该点连接,权值为该点的点权,
if(i+j)%2==1 该点和汇点连接,权值为该点的点权,
之后若i+j为偶数的点和i+j为奇数的点之间相邻,那么就连一条从为偶数的点到为奇数的点的边,权值为无穷大
之后 所有权值-最大流 即可
#include <stdio.h> #include <string.h> #define VM 2555 #define EM 2055000 #define inf 0x3f3f3f3f struct Edge { int frm,to,cap,next; }edge[EM]; int head[VM],dep[VM],ep; //dep为点的层次 void addedge (int cu,int cv,int cw) //第一条边下标必须为偶数 { edge[ep].frm = cu; edge[ep].to = cv; edge[ep].cap = cw; edge[ep].next = head[cu]; head[cu] = ep; ep ++; edge[ep].frm = cv; edge[ep].to = cu; edge[ep].cap = 0; edge[ep].next = head[cv]; head[cv] = ep; ep ++; } int BFS (int src,int des) //求出层次图 { int que[VM],i,front = 0,rear = 0; memset (dep,-1,sizeof(dep)); que[rear++] = src; dep[src] = 0; while (front != rear) { int u = que[front++]; front = front%VM; for (i = head[u];i != -1;i = edge[i].next) { int v = edge[i].to; if (edge[i].cap > 0&&dep[v] == -1) //容量大于0&&未在dep中 { dep[v] = dep[u] + 1; //建立层次图 que[rear ++] = v; rear = rear % VM; if (v == des) //找到汇点 返回 return 1; } } } return 0; } int dinic (int src,int des) { int i,res = 0,top; int stack[VM]; //stack为栈,存储当前增广路 int cur[VM]; //存储当前点的后继 跟head是一样的 while (BFS(src,des)) //if BFS找到增广路 { memcpy (cur,head,sizeof (head)); int u = src; //u为当前结点 top = 0; while (1) { if (u == des) //增广路已全部进栈 { int min = inf,loc ; for (i = 0;i < top;i ++) //找最小的增广跟并loc记录其在stack中位置 if (min > edge[stack[i]].cap) //以便退回该边继续DFS { min = edge[stack[i]].cap; loc = i; } for (i = 0;i < top;i ++) //偶数^1 相当加1 奇数^1相当减1 当正向边 = 0&&路径不合适时,正加负减 { //偶数是正向边,奇数是负向边,边从0开始 edge[stack[i]].cap -= min; edge[stack[i]^1].cap += min; } //将增广路中的所有边修改 res += min; top = loc; u = edge[stack[top]].frm; //当前结点修改为最小边的起点 } for (i = cur[u];i != -1;cur[u] = i = edge[i].next) //找到当前结点对应的下一条边 if (edge[i].cap != 0&&dep[u] + 1 == dep[edge[i].to])//不满足条件时,修改cur值(去掉不合适的占)eg:1-->2 1-->3 1-->4 有边 但只有 break; // 1-->4 这条边满足条件 就把1到2、3的边给去掉 if (cur[u] != -1) //当前结点的下一条边存在 { stack[top ++] = cur[u]; //把该边放入栈中 u = edge[cur[u]].to; //再从下个点开始找 } else { if (top == 0) //当前结点无未遍历的下一条边且栈空,DFS找不到下一条增广路 break; dep[u] = -1; //当前结点不在增广路中,剔除该点 u = edge[stack[--top]].frm; //退栈 回朔,继续查找 } } } return res; } int dir[4][2]={0,1,0,-1,1,0,-1,0}; int x,y; int main ()///坐标从0或1开始均可 注意别忘记下面的2个初始化 { int m,n; int src,des; int sum=0; while (scanf ("%d %d",&m,&n)!=EOF) { sum=0; ep = 0;//边的初始化 src =n*m; des =m*n+1; memset (head,-1,sizeof(head));///这里初始化 for(int i=0;i<m;i++) for(int j=0;j<n;j++) { int mid; scanf("%d",&mid); sum+=mid; //printf("%d\n",i*m+j); if((i+j)%2==0) addedge(src,i*n+j,mid); else addedge(i*n+j,des,mid); } for(int i=0;i<m;i++) { for(int j=0;j<n;j++) { for(int k=0;k<4;k++) { x=i+dir[k][0]; y=j+dir[k][1]; if(x>=0&&x<m&&y>=0&&y<n) { if(((x+y)%2)!=((i+j)%2)) { if((x+y)%2==1) addedge(i*n+j,x*n+y,inf); else addedge(x*n+y,i*n+j,inf); } } } } } // printf("sum=%d\n",sum); int ans = dinic (src,des); printf ("%d\n",sum-ans); } return 0; }