1324: Exca王者之剑
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 618 Solved: 310
[Submit][Status][Discuss]
Description
Input
第一行给出数字N,M代表行列数.N,M均小于等于100 下面N行M列用于描述数字矩阵
Output
输出最多可以拿到多少块宝石
Sample Input
2 2
1 2
2 1
1 2
2 1
Sample Output
4
改一下方格尺寸这个题就和方格取数一样了
那么我们来写题解
旨在讲清楚最大点权独立集(最小点权覆盖)的一般解法
题目显然在要求一个最大点权独立集(概念请见传送门)
最大点权独立集与最小点权覆盖是对偶问题,这里先介绍最小点权覆盖的解法。
最小点权覆盖问题是指:
给出一张二分图,二分图的每个节点带有一个点权,要求从中选出若干节点
使得这些节点能够覆盖二分图中所有的边,并使得节点的权值和最小。
该类问题可用网络流最小割算法来解决。
考虑最小割的性质,最小割能够将原图中所有的点划分为两个集合,这能够与最小点权覆盖问题中的点得选中与否对应
如果能够找到一种建模方式
满足在二分图中的每条边连接的两个点中,至少一个被选中,就能使网络流的最小割与二分图的最小点权覆盖相匹配。
于是考虑如下建模方式:
建立源点S,汇点T,保留二分图中的所有节点xi,yi,从S到所有xi连边,权值为xi的权值;
若二分图中xi到yi有边,在网络流图中从xi向yi连边,权值为无穷;从所有yi到T连边,权值为yi的权值。
这样,二分图中的每条边连接的两个点中,要么与S形成割边,要么与T形成割边(因为这两个点之间的边权为inf)
这就保证了两个点中,至少一个被选中,因此最小割就对应了一个最小点权的选点方案。
故网络流图中的最小割就是原问题的最小点权。
而最大点权独立集问题中,需要满足在二分图中的每条边连接的两个点中,选择至多一个,最终是选择的点的点权和最大
换句话说,就是在二分图中的每条边连接的两个点中,至少不选择一个,使得不选择的点的点权和最小。
说到这里,大家应该能看出最大点权独立集合最小点权覆盖问题的联系了:
最大点权独立集=权值和-最小点权覆盖
在了解了解法之后就当然会做啦
我们可以将方格拆成一个二分图;
对于点(i,j):
如果(i+j)为奇数,我们就将其向源点连边;
如果(i+j)为偶数,我们就将其向汇点连边;
我们可以把不能在同一个集合里的点之间连边(也就是上述的两类点),边权设置为无穷大;
然后求出最小割。
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<queue>
#include<set>
#include<map>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f*x;
}
const int N=10010;const int inf=0X7f7f7f7f;
int n,m,mp[110][110],ecnt=1,last[N],d[N],q[N],ans,S,T=N-2;
struct EDGE{int to,nt,val;}e[N<<5];
inline void readd(int u,int v,int val)
{e[++ecnt]=(EDGE){v,last[u],val};last[u]=ecnt;}
inline void add(int u,int v,int val)
{readd(u,v,val);readd(v,u,0);}
bool bfs()
{
memset(d,0,sizeof(d));
d[S]=1;int head=0,tail=1;q[0]=S;
while(head<tail)
{
int u=q[head++];
for(int i=last[u];i;i=e[i].nt)
if(e[i].val&&!d[e[i].to])
{
d[e[i].to]=d[u]+1;
q[tail++]=e[i].to;
}
}
return d[T];
}
int dfs(int u,int lim)
{
if(u==T||!lim)return lim;
int flow=0;
for(int i=last[u];i;i=e[i].nt)
if(d[e[i].to]==d[u]+1)
{
int tmp=dfs(e[i].to,min(e[i].val,lim));
flow+=tmp;lim-=tmp;e[i].val-=tmp;e[i^1].val+=tmp;
if(!lim)break;
}
if(!flow)d[u]=-1;
return flow;
}
void dinic()
{while(bfs()){ans+=dfs(S,inf);}}
int main()
{
n=read();m=read();int sum=0;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
{
mp[i][j]=read();sum+=mp[i][j];
if((i+j)%2)
{
add(S,(i-1)*m+j,mp[i][j]);
if(j>1)add((i-1)*m+j,(i-1)*m+j-1,inf);
if(i>1)add((i-1)*m+j,(i-2)*m+j,inf);
}
else
{
add((i-1)*m+j,T,mp[i][j]);
if(j>1)add((i-1)*m+j-1,(i-1)*m+j,inf);
if(i>1)add((i-2)*m+j,(i-1)*m+j,inf);
}
}
dinic();
printf("%d\n",sum-ans);
return 0;
}