Description
在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
编程任务:
对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
Input
第1 行有2 个正整数m和n,分别表示棋盘的行数和列数。
接下来的m行,每行有n个正整数,表示棋盘方格中的数。
Output
程序运行结束时,将取数的最大总和输出
Sample Input
3 3
1 2 3
3 2 3
2 3 1
Sample Output
11
题目链接:
这个题是一个很经典的问题:最大点权问题
二分图最大点权独立集问题,就是找出图中一些点,使得这些点之间没有边相连,这些点的权值之和最大。
独立集与覆盖集是互补的,求最大点权独立集可以转化为求最小点权覆盖集(最小点权支配集)。最小点权覆盖集问题可以转化为最小割问题解决。
结论:最大点权独立集 = 所有点权 - 最小点权覆盖集 = 所有点权 - 最小割集 = 所有点权 - 网络最大流。
二分图最大点权独立集问题,更多讨论见《最小割模型在信息学竞赛中的应用》作者胡伯涛
这里不会证明
说说思路:把这个图黑白染色,使得任意一个黑点的相邻四个点是白点,任意一个白点的相邻四个点是黑点
添加源S和汇T
S向所有的黑点连接一条容量为黑点格子中数值的有向边(很经典的思路,化格子中的点权为S的边权)
所有的黑点向T连接一条容量为白点格子中数值的有向边
然后,对于相邻的两个点,从黑点向白点连接一条容量为INF的有向边
这里,就只贴出来黑白染色的代码和连边的代码了
int main(){
//freopen("input.txt","r",stdin);
int m,n,s,t,tot,ans=0,x,y;
init();
scanf("%d%d",&n,&m);
s=n*m;t=n*m+1;tot=n*m+2;x=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if (i%2==0)
num[i][j]=x++;
else
num[i][m-j-1]=x++;
//for(int i=0;i<n;i++)
// for(int j=0;j<m;j++)
// printf("%d%c",num[i][j],j==m-1?'\n':' ');
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&mp[i][j]);
//for(int i=0;i<n;i++)
// for(int j=0;j<m;j++)
// printf("%d%c",mp[i][j],j==m-1?'\n':' ');
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
ans+=mp[i][j];
if (num[i][j]%2==0){
if (i<n-1) addedge(num[i][j],num[i+1][j],INF);
if (j<m-1) addedge(num[i][j],num[i][j+1],INF);
if (i>0) addedge(num[i][j],num[i-1][j],INF);
if (j>0) addedge(num[i][j],num[i][j-1],INF);
addedge(s,num[i][j],mp[i][j]);
}
else{
addedge(num[i][j],t,mp[i][j]);
}
}
//printf("%d\n",ans);
int maxflow=dinic(s,t,tot);
//printf("%d\n",maxflow);
printf("%d\n",ans-maxflow);
return 0;
}