hdu的1565也可以用同样的代码、将m=n就可以了。其他的照样不变
题意描述:你一个m*n的格子的棋盘,每个
格子里面有一个非负数。从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大
分析:该题求一个二分图的最大权独立集,那么如何建立二分图呢?考虑到最大权独立集和最小点权覆盖集是互补的,那么我们就可以将该问题转化为最小点权覆盖了,从而转换为最小割问题!下面给出两种建图的方法:
第一种:根据奇偶建立二分图,如果(i+j)%2==0 那么可以是该点和源点连接,其余的和汇点连接,权值均为该点的点权,之后若i+j为偶数的点和i+j为奇数的点之间相邻,那么就连一条从为偶数的点到为奇数的点的边,权值为无穷大
第二种:拆点,将每个点权拆为两个点u,u’如果两个点相邻,那么连一条边从u到v’,权值为无穷大,u连接源点,权值为点权,u’连接汇点,权值也为点权
对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有:
最大点权独立集 + 最小点权覆盖集 = 总点权和,
这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图,
1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一条边,权值为该黑色点的权值,
2,从白色的点向汇点连一条边,权值为该白色点的权值,
3,然后,对于每一对相邻的黑白点,从黑点向白点连一条边,权值为无穷大。
最后求最小割(最大流),即为最小点权覆盖集。
因为我们求出的最小割集一定是从那些相邻的黑白点之间的边(也就是不能用的边,因为相邻的数不能同时选)中选出来的,且是最小代价,也就是说从方格中拿掉的数之和尽量小,那么剩下的数之和一定是最大的。
自写的代码,实现效率和第一种其实是差不多的也是0MS 用Isap做的
#include<iostream>
#include<cstdio>
#include<memory.h>
#include<cmath>
using namespace std;
#define MAXN 5010
#define MAXE 1000100
#define INF 0x3fffffff
int ne,nv,tmp,s,t,index;
struct Edge{
int next,pair,v;
int cap,fLow;
}edge[MAXE];
int net[MAXN];
int ISAP()
{
int numb[MAXN],dist[MAXN],curedge[MAXN],pre[MAXN];
int cur_fLow,max_fLow;
int u,tmp,neck,i;
memset(dist,0,sizeof(dist));
memset(numb,0,sizeof(numb));
memset(pre,-1,sizeof(pre));
for(i = 1 ; i <= nv ; ++i)
curedge[i] = net[i];
numb[nv] = nv;
max_fLow = 0;
u = s;
while(dist[s] < nv)
{
if(u == t)
{
cur_fLow = INF+1;
for(i = s; i != t;i = edge[curedge[i]].v)
{
if(cur_fLow > edge[curedge[i]].cap)
{
neck = i;
cur_fLow = edge[curedge[i]].cap;
}
}
for(i = s; i != t; i = edge[curedge[i]].v)
{
tmp = curedge[i];
edge[tmp].cap -= cur_fLow;
edge[tmp].fLow += cur_fLow;
tmp = edge[tmp].pair;
edge[tmp].cap += cur_fLow;
edge[tmp].fLow -= cur_fLow;
}
max_fLow += cur_fLow;
u = neck;
}
for(i = curedge[u]; i != -1; i = edge[i].next)
if(edge[i].cap > 0 && dist[u] == dist[edge[i].v]+1)
break;
if(i != -1)
{
curedge[u] = i;
pre[edge[i].v] = u;
u = edge[i].v;
}else{
if(0 == --numb[dist[u]]) break;
curedge[u] = net[u];
for(tmp = nv,i = net[u]; i != -1; i = edge[i].next)
if(edge[i].cap > 0)
tmp = tmp<dist[edge[i].v]?tmp:dist[edge[i].v];
dist[u] = tmp + 1;
++numb[dist[u]];
if(u != s) u = pre[u];
}
}
return max_fLow;
}
void addedge(int u,int v,int f)
{
edge[index].next = net[u];
edge[index].v = v;
edge[index].cap = f;
edge[index].fLow = 0;
edge[index].pair = index+1;
net[u] = index++;
edge[index].next = net[v];
edge[index].v = u;
edge[index].cap = 0;
edge[index].fLow = 0;
edge[index].pair = index-1;
net[v] = index++;
}
int main() {
int i,j,m,n;
int sum;
while(scanf("%d%d",&n,&m)!=EOF)
{
int c;
sum=0;
index=0;
s = 0;
t = 2*n*m+1;
nv=t+1;
memset(net,-1,sizeof(net));
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
scanf("%d",&c);
sum+=c;
addedge(s,(i-1)*m+j,c);//建立每个点到源点的
addedge((i-1)*m+j+n*m,t,c);//建立每个点的次点到汇点的
if(i>1 && i<=n && j<=m) //建立与该点上面的
addedge((i-1)*m+j,(i-2)*m+n*m+j,INF);
if(j>1 && i<=n && j<=m) //建立与该点左边的
addedge((i-1)*m+j, (i-1)*m+j-1+n*m,INF);
if(i<n && i>=1 && j<=m) //建立与该点下面的
addedge((i-1)*m+j,i*m+n*m+j,INF);
if(j<m && j>=1 && i<=n) //建立与该点右边的
addedge((i-1)*m+j,(i-1)*m+j+n*m+1,INF);
}
int ans=ISAP();
printf("%d\n",sum-ans/2);//因为拆点了所以流量要除以2!
}
return 0;
}
网上找的两种实现代码,注释的是第一种。
<SPAN style="FONT-FAMILY: SimHei; FONT-SIZE: 16px">/*#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf = 0x3fffffff;
const int N = 2600;
const int E = 30000;
int e,head[N];
int dep[N],que[N],cur[N];
struct node
{
int x,y;
int nxt;
int c;
}edge[E];
void addedge(int u,int v,int c)
{
edge[e].x=u;
edge[e].y=v;
edge[e].nxt=head[u];
edge[e].c=c;
head[u]=e++;
edge[e].x=v;
edge[e].y=u;
edge[e].nxt=head[v];
edge[e].c=0;
head[v]=e++;
}
int maxflow(int s,int t)
{
int i,j,k,front,rear,top,min,res=0;
while(1)
{
memset(dep,-1,sizeof(dep));
front=0;
rear=0;
que[rear++]=s;
dep[s]=0;
while(front!=rear)
{
i=que[front++];
for(j=head[i];j!=-1;j=edge[j].nxt)
if(edge[j].c&&dep[edge[j].y]==-1)
{
dep[edge[j].y]=dep[i]+1;
que[rear++]=edge[j].y;
}
}
if(dep[t]==-1)
break;
memcpy(cur,head,sizeof(head));
for(i=s,top=0;;)
{
if(i==t)
{
min=inf;
for(k=0;k<top;k++)
if(min>edge[que[k]].c)
{
min=edge[que[k]].c;
front=k;
}
for(k=0;k<top;k++)
{
edge[que[k]].c-=min;
edge[que[k]^1].c+=min;
}
res+=min;
i=edge[que[top=front]].x;
}
for(j=cur[i];cur[i]!=-1;j=cur[i]=edge[cur[i]].nxt)
if(dep[edge[j].y]==dep[i]+1&&edge[j].c)
break;
if(cur[i]!=-1)
{
que[top++]=cur[i];
i=edge[cur[i]].y;
}
else
{
if(top==0)
break;
dep[i]=-1;
i=edge[que[--top]].x;
}
}
}
return res;
}
int main()
{
int n,m,i,j,c,sum;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,-1,sizeof(head));
e = 0;
int src = 0;
int sin = n*m+1;
sum = 0;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
scanf("%d",&c);
sum+=c;
if((i+j)%2 == 0)
{
addedge(src,(i-1)*m+j,c);
if(i>1)
addedge((i-1)*m+j,(i-2)*m+j,inf);
if(j>1)
addedge((i-1)*m+j,(i-1)*m+j-1,inf);
if(i<n)
addedge((i-1)*m+j,i*m+j,inf);
if(j<m)
addedge((i-1)*m+j,(i-1)*m+j+1,inf);
}
else addedge((i-1)*m+j,sin,c);
}
// cout << sum <<endl;
printf("%d\n",sum-maxflow(src,sin));
}
return 0;
}
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 5100;
const int E = 80000;
const int inf = 0x3fffffff;
int e,head[N];
int dep[N],que[N],cur[N];
struct node
{
int x,y;
int nxt;
int c;
}edge[E];
void addedge(int u,int v,int c)
{
edge[e].x=u;
edge[e].y=v;
edge[e].nxt=head[u];
edge[e].c=c;
head[u]=e++;
edge[e].x=v;
edge[e].y=u;
edge[e].nxt=head[v];
edge[e].c=0;
head[v]=e++;
}
int maxflow(int s,int t)
{
int i,j,k,front,rear,top, min,res=0;
while(1)
{
memset(dep,-1,sizeof(dep));
front=0;
rear=0;
que[rear++]=s;
dep[s]=0;
while(front!=rear)
{
i=que[front++];
for(j=head[i];j!=-1;j=edge[j].nxt)
if(edge[j].c&&dep[edge[j].y]==-1)
{
dep[edge[j].y]=dep[i]+1;
que[rear++]=edge[j].y;
}
}
if(dep[t]==-1)
break;
memcpy(cur,head,sizeof(head));
for(i=s,top=0;;)
{
if(i==t)
{
min=inf;
for(k=0;k<top;k++)
if(min>edge[que[k]].c)
{
min=edge[que[k]].c;
front=k;
}
for(k=0;k<top;k++)
{
edge[que[k]].c-=min;
edge[que[k]^1].c+=min;
}
res+=min;
i=edge[que[top=front]].x;
}
for(j=cur[i];cur[i]!=-1;j=cur[i]=edge[cur[i]].nxt)
if(dep[edge[j].y]==dep[i]+1&&edge[j].c)
break;
if(cur[i]!=-1)
{
que[top++]=cur[i];
i=edge[cur[i]].y;
}
else
{
if(top==0)
break;
dep[i]=-1;
i=edge[que[--top]].x;
}
}
}
return res;
}
int main ()
{
int n, m,i,j;
int sum, c;
while(scanf("%d%d",&n,&m)!=EOF)
{
sum = 0;
int src = 0;
int sin = 2*n*m+1;
e =0;
memset(head,-1,sizeof(head));
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
scanf("%d",&c);
sum += c;
addedge(src,(i-1)*m+j,c);//建立源点到每个点的
addedge((i-1)*m+j+n*m,sin,c);
if(i>1)
addedge((i-1)*m+j,(i-2)*m+n*m+j,inf);
if(j>1)
addedge((i-1)*m+j, (i-1)*m+j-1+n*m,inf);
if(i<n)
addedge((i-1)*m+j,i*m+n*m+j,inf);
if(j<m)
addedge((i-1)*m+j,(i-1)*m+j+n*m+1,inf);
}
int flow = maxflow(src,sin);
printf("%d\n",sum-flow/2);
}
return 0;
}