方格取数(1)
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8656 Accepted Submission(s): 3299
Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3 75 15 21 75 15 28 34 70 5
Sample Output
188
Author
ailyanlu
Source
Recommend
经典的最大点权独立集问题。
- 定理: https://wenku.baidu.com/view/986baf00b52acfc789ebc9a9.html
- 最小割=最大流=最小点权覆盖集=sum - 最大点权独立集
二分图最小点权覆盖
从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。
建模:
原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t,将s和x集合中的点相连,容量为该点的权值;将y中的点同t相连,容量为该点的权值。在新图上求最大流,最大流量即为最小点权覆盖的权值和。
二分图最大点权独立集
在二分图中找到权值和最大的点集,使得它们之间两两没有边。其实它是最小点权覆盖的对偶问题。答案=总权值-最小点权覆盖集
覆盖集。具体证明参考胡波涛的论文。
论文网址:https://wenku.baidu.com/view/986baf00b52acfc789ebc9a9.html
#include <bits/stdc++.h>
using namespace std;
const int MAXN=400+5;
const int inf=1e9+7;
int n;
int s,e;
int tu[25][25];
int dx[4]= {0,0,-1,1};
int dy[4]= {-1,1,0,0};
struct node
{
int u,v,f;
int next;
} edge[MAXN*520];
int head[MAXN];
int cnt;
void add(int u,int v,int f)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].f=f;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].f=0;
edge[cnt].next=head[v];
head[v]=cnt++;
}
int dis[MAXN];
int bfs(int s,int t)
{
int u, v ;
memset(dis,-1,sizeof(dis));
dis[s] = 0 ;
queue<int>q;
q.push(s) ;
while( !q.empty() )
{
u = q.front();
q.pop();
for(int i = head[u] ; i != -1 ; i = edge[i].next)
{
v = edge[i].v ;
if( dis[v] == -1 && edge[i].f )
{
dis[v] = dis[u] + 1 ;
q.push(v) ;
}
}
}
if( dis[t] > 0 )
return 1 ;
return 0 ;
}
int dfs(int s,int t,int min1)
{
if( s == t )
return min1 ;
int flow, ans = 0 ;
for(int i = head[s] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v ;
if( dis[v] == dis[s] + 1 && edge[i].f && (flow = dfs(v,t,min(min1,edge[i].f) ) ) )
{
edge[i].f -= flow ;
edge[i^1].f += flow ;
ans += flow ;
min1 -= flow ;
if( !min1 )
break;
}
}
if( ans )
return ans ;
dis[s] = -1 ;
return 0;
}
int getMaxFlow()
{
int maxFlow=0,flow;
while(bfs(s,e))
{
while((flow=dfs(s,e,inf))>0)
maxFlow+=flow;
}
return maxFlow;
}
int main()
{
while(~scanf("%d",&n))
{
s=0;
e=n*n+1;
int sum=0;
cnt=0;
memset(head,-1,sizeof(head));
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
{
scanf("%d",&tu[i][j]);
sum += tu[i][j];
if((i + j)%2 == 0)add(0, (i-1)*n+j, tu[i][j]);
else add((i-1)*n+j, e, tu[i][j]);
for(int k=0; k<4; ++k)
{
int nx=i+dx[k];
int ny=j+dy[k];
if(nx>=1&&nx<=n&&ny>=1&&ny<=n&&(i+j)%2==0)
add((i-1)*n+j,(nx-1)*n+ny,inf);
}
}
printf("%d\n",sum-getMaxFlow());
}
return 0;
}
又学习了~