大致题意:
给出一个n*m的矩阵,让你从中取出一定数量的数字。如果在矩阵中两两相邻的数字被取到的话需要付出一定的代价。而且给出某些点,规定这些点一定需要取到。求最多可以取到多少点。
大致思路:
怎么说呢,这道题乍看上去和hdoj 1569:方格取数很相似,也很像是一个二分图的最大点权独立集问题。但是问题出的很巧妙,也就没有办法往模版上面套了。把矩阵中的点按照横纵坐标之和的奇偶性分成两个集合,设超级源汇点,源点第一个集合中的所有点连边,容量为这个点代表的数字的值。第二个集合中的所有点向汇点连边,容量也是这个点的值。第一个集合中点都向他周围的点连边,容量为他们同时被取时的消耗。如果一个点必须取,那就将他和源/汇点的容量设为inf,保证这条边不被割掉。用所有点的权值之和sum减去这个图的最小割得到的就是答案。总的来说,ac后的感受就是,这是一道需要意识流的题目
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=100001 ;
const int M=N*5 ;
const int inf=1<<30 ;
struct node
{
int u,v,c,next;
}edge[M];
int head[N],gap[N],dis[N],cur[N],pre[N];
int top ;
void add(int u,int v,int c)
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].next=head[u];
head[u]=top++;
edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].next=head[v];
head[v]=top++;
}
int sap(int s,int t,int n)
{
int flow=0,aug=inf,u;
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
for(int i=0; i<=n; i++) cur[i]=head[i];
gap[s]=n;
u=pre[s]=s;
while(dis[s]<n)
{
loop :
for(int &j=cur[u]; j!=-1; j=edge[j].next)
{
int v=edge[j].v;
if(edge[j].c>0&&dis[u]==dis[v]+1)
{
if(edge[j].c<aug) aug=edge[j].c;
pre[v]=u;
u=v;
if(v==t)
{
while(u!=s)
{
u=pre[u];
edge[cur[u]].c-=aug;
edge[cur[u]^1].c+=aug;
}
flow+=aug;
aug=inf;
}
goto loop ;
}
}
int mindis=n;
for(int j=head[u]; j!=-1; j=edge[j].next)
{
int v=edge[j].v;
if(edge[j].c>0&&dis[v]<mindis)
{
mindis=dis[v];
cur[u]=j;
}
}
if((--gap[dis[u]])==0)
break;
gap[dis[u]=mindis+1]++;
u=pre[u];
}
return flow;
}
int main()
{
int n,m,k,a,b ;
int x[100][100];
while(~scanf("%d%d%d",&n,&m,&k))
{
top=0;
memset(head,-1,sizeof(head));
int s=0,t=n*m+1 ,sum=0;
for(int i=1;i<=n;i++)
for(int j=1;j <=m; j++)
{
scanf("%d",&x[i][j]);
int tmp=(i-1)*m+j;
if((i+j)%2==0) //连源点汇点
add(s,tmp,x[i][j]);
else
add(tmp,t,x[i][j]);
sum += x[i][j];
}
for(int i=1;i<=k;i++)
{
scanf("%d%d",&a,&b);
int tmp=(a-1)*m+b;
if((a+b)%2==0) //规定的点,容量改为inf,避免被割,
add(s,tmp,inf);
else
add(tmp,t,inf);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if((i+j)%2==0)
{
int tmp=(i-1)*m+j; //相邻损失的分数为 2(a&b);
if(i>1) add(tmp,tmp-m,2*(x[i][j]&x[i-1][j])) ;
if(i<n) add(tmp,tmp+m,2*(x[i][j]&x[i+1][j])) ;
if(j>1) add(tmp,tmp-1,2*(x[i][j]&x[i][j-1])) ;
if(j<m) add(tmp,tmp+1,2*(x[i][j]&x[i][j+1])) ;
}
int ans=sap(s,t,t+1);
printf("%d\n",sum-ans) ;
}
return 0;
}