中文题意不解释。。
要懂这道题,要先懂得一些基本的概念。。在我的博客里面也有这些概念。。我这里再重复一下,也说明这些概念的重要性。。
基于二分图性质的一些推论,可利用最大流+最小切割解决一类网络流问题: HDU 1565 方格取数(1)
题目大意:
给定一个尺寸的方格,每个格子拥有一个非负数,求一种满足题目约束的取数方法,最重使得取数总和最大。约束规则为,任意两个被取的格子间不能拥有公共边,也就是不能相邻。
那么这个问题怎么解决呢?很多童鞋做网络流的时候,一旦没有思路就上网搜解题报告,个人觉得你网路流的构图都是别人告诉你了,这题基本就作废了~
先来回顾以下几个推论:
点覆盖集:无向图G的一个点集,使得该图中所有边都至少有一个端点在该点集中。
最小点权覆盖集:在带点权无向图G中,点权和最小的点覆盖集。
点独立集:无向图G的一个点集,使得任何两个在点集中的点在图G中都不相邻。
最大点权独立集:在无向带权图G中,点权和最大的点独立集。
最小点权覆盖集=最小割=最大流
最大点权独立集=总权-最小点权覆盖集
——————> 胡伯涛《最小割模型在信息学竞赛中的应用》
于是回到这个问题中,我们可以看到,不存在任意两个被取方格存在公共边,如果将方格抽象成顶点,相邻关系抽象成边,那么约束就转变成了相邻顶点不能同时被选取,原问题转化成为求一个图上的顶点独立集。由于相邻顶点黑白染色之后,方格可被转化为二分图,在二分图上求顶点独立集就简单了。由”最大点独立集=全集-最小点覆盖集”关系,问题变成求解二分图的最小顶点覆盖,最后覆盖集的补集为所求。这就是这个问题可由网络流解决的论证思路。其实由于网络流完成后最小割集合对应”不取集合”的最小解,那么自然也就使得可取的集合解最大了。
构图方法 : 方格顶点黑白染色,黑点向相邻白点连有向弧,容量正无穷;所有黑点被源点连,容量对用方格数;所有白点连向汇点,
容量方格数
然后就可以根据上面的方法来做了。
//这道题用EK会TLE,,要用ISPA,DINIC。。。
//还有就是要学会黑白染色的两种方法。。
//还有就是模板的灵活运用。。
我下面给出两种染色的方法:
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define inf 0x7FFFFFFF
const int N=410; //这里比较坑爹,,要开大一点。。
const int INF = 0x7FFFFFFF;
int move[4][2]={0, 1, 0, -1, 1, 0, -1, 0};
int n,s,t,dis[N],pre[N],gap[N],flow[N][N];
struct edge
{
int v,w;
edge *next,*rev;
edge(){next=0;}
edge(int vv,int ww,edge *e)
{v=vv;w=ww;next=e;}
}*adj[N],*path[N],*e;
int min(int x,int y)
{
if(x>y)
return y;
else
return x;
}
void insert(int u,int v,int w)
{
edge *p=new edge(v,w,adj[u]);
adj[u]=p;
edge *q=new edge(u,0,adj[v]);
adj[v]=q;
p->rev=q;
q->rev=p;
}
void bfs()
{
memset(dis,0x7f,sizeof(dis));
memset(gap,0,sizeof(gap));
queue<int> q;
dis[t]=0;
gap[0]=1;
q.push(t);
while(q.size())
{
int x=q.front();
q.pop();
for(e=adj[x];e;e=e->next)
{
if(e->rev->w==0||dis[e->v]<t)
continue;
dis[e->v]=dis[x]+1;
++gap[dis[e->v]];
q.push(e->v);
}
}
}
int ISAP()
{
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
//bfs();
int ans=0,u=s,d;
while(dis[s]<=t)
{
if(u==t)
{
int minflow=-1u>>1;
for(e=path[u];u!=s;e=path[u=pre[u]])
minflow=min(minflow,e->w);
for(e=path[u=t];u!=s;e=path[u=pre[u]])
{
e->w-=minflow;
e->rev->w+=minflow;
flow[pre[u]][u]+=minflow;
flow[u][pre[u]]-=minflow;
}
ans+=minflow;
}
for(e=adj[u];e;e=e->next)
if(e->w>0&&dis[u]==dis[e->v]+1)
break;
if(e)
{
pre[e->v]=u;
path[e->v]=e;
u=e->v;
}
else
{
if(--gap[dis[u]]==0)
break;
for(d=t,e=adj[u];e;e=e->next)
if(e->w>0)
d=min(d,dis[e->v]);
dis[u]=d+1;
++gap[dis[u]];
if(u!=s)
u=pre[u];
}
}
return ans;
}
int main()
{
int i,j;
int sum;
int temp;
while(scanf("%d",&n)!=EOF)
{
sum=0;
memset(adj,0,sizeof(adj));//这句要记得加哦。。
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
scanf("%d",&temp);
if ((i+j)%2==0)
insert(0,(i-1)*n+j,temp);//坐标和为偶数的点和源点0相连
else
insert((i-1)*n+j,n*n+1,temp);//坐标和为奇数数的点和汇点n*n+1相连
sum+=temp;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if((i+j)%2==0)
{
for(int k=0;k<4;k++)
{
int a=i+move[k][0];
int b=j+move[k][1];
if (a>=1&&a<=n&&b>=1&&b<=n)
{
insert((i-1)*n+j,(a-1)*n+b,INF);
}
}
}
s=0,t=n*n+1;
printf("%d\n",sum-ISAP());
}
return 0;
}
/*
下面是另一种染色方法:
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define inf 0x7FFFFFFF
const int N=410;
const int INF = 0x7FFFFFFF;
int move[4][2]={0, 1, 0, -1, 1, 0, -1, 0};
int n,s,t,dis[N],pre[N],gap[N],flow[N][N];
int res[N][N],cor[N][N];
struct edge
{
int v,w;
edge *next,*rev;
edge(){next=0;}
edge(int vv,int ww,edge *e)
{v=vv;w=ww;next=e;}
}*adj[N],*path[N],*e;
int min(int x,int y)
{
if(x>y)
return y;
else
return x;
}
void insert(int u,int v,int w)
{
edge *p=new edge(v,w,adj[u]);
adj[u]=p;
edge *q=new edge(u,0,adj[v]);
adj[v]=q;
p->rev=q;
q->rev=p;
}
void bfs()
{
memset(dis,0x7f,sizeof(dis));
memset(gap,0,sizeof(gap));
queue<int> q;
dis[t]=0;
gap[0]=1;
q.push(t);
while(q.size())
{
int x=q.front();
q.pop();
for(e=adj[x];e;e=e->next)
{
if(e->rev->w==0||dis[e->v]<t)
continue;
dis[e->v]=dis[x]+1;
++gap[dis[e->v]];
q.push(e->v);
}
}
}
int ISAP()
{
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
//bfs();
int ans=0,u=s,d;
while(dis[s]<=t)
{
if(u==t)
{
int minflow=-1u>>1;
for(e=path[u];u!=s;e=path[u=pre[u]])
minflow=min(minflow,e->w);
for(e=path[u=t];u!=s;e=path[u=pre[u]])
{
e->w-=minflow;
e->rev->w+=minflow;
flow[pre[u]][u]+=minflow;
flow[u][pre[u]]-=minflow;
}
ans+=minflow;
}
for(e=adj[u];e;e=e->next)
if(e->w>0&&dis[u]==dis[e->v]+1)
break;
if(e)
{
pre[e->v]=u;
path[e->v]=e;
u=e->v;
}
else
{
if(--gap[dis[u]]==0)
break;
for(d=t,e=adj[u];e;e=e->next)
if(e->w>0)
d=min(d,dis[e->v]);
dis[u]=d+1;
++gap[dis[u]];
if(u!=s)
u=pre[u];
}
}
return ans;
}
int main()
{
int i,j;
int sum;
int temp;
while(scanf("%d",&n)!=EOF)
{
sum=0;
memset(adj,0,sizeof(adj));//这句要记得加哦。。
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
cin>>res[i][j];
sum+=res[i][j];
}
for(i=1;i<=n;i+=2)
cor[1][i]=1;
for(i=2;i<=n;i++)
for(j=1;j<=n;j++)
cor[i][j]=!cor[i-1][j];
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
if(cor[i][j]==0)
{
insert(0,(i-1)*n+j,res[i][j]);
}
else
if(cor[i][j]==1)
{
insert((i-1)*n+j,n*n+1,res[i][j]);
}
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(cor[i][j]==0)
{
for(int k=0;k<4;k++)
{
int a=i+move[k][0];
int b=j+move[k][1];
if(a>=1&&a<=n&&b>=1&&b<=n)
{
insert((i-1)*n+j,(a-1)*n+b,INF);
}
}
}
s=0,t=n*n+1;
printf("%d\n",sum-ISAP());
}
return 0;
}*/