「Luogu P3866」[TJOI2009]战争游戏 解题报告

题面

好难表述啊~

在n*m的矩阵上,有一些大兵(为0),一些空地(一个正整数),障碍物(-1),现在摧毁一些空地,使所有大兵不能走出矩阵去(代价为表示空地的整数),求最小代价

思路:

网络流最小割

“阻止”,“最小”,看到这样的字眼,肯定就要想到最小割啊

在互相能到达的点之间建边,容量为INF,因为——它不能炸……

然后把每个点拆成入点出点,每个兵所在的出点和源点S直接相连,在最外面的点的出点和汇点T直接相连

最后套模板,OK了

最重要的还是建边,能够理解题目的意思,想出对应的策略

Code:

#include<bits/stdc++.h>
#define INF 0x7f7f7f7f
#define M 5010
#define N 2010
#define Mn 31
using namespace std;
int Cx[4]={1,0,0,-1};//预处理移动的方向
int Cy[4]={0,1,-1,0};
struct node{
	int to,cap;
	int nxt;
	node(int a,int b):to(a),cap(b){	}
	node(){	}
}b[M<<1];
int head[N],deep[N],a[Mn][Mn];
int n,m,S,T,Maxflow,Max,t=1;
bool p[Mn][Mn];
int read()
{
	int s=0,p=1;
	char c=getchar();
	while(!isdigit(c))
	{
		if(c=='-')
			p=-1;
		c=getchar();
	}
	while(isdigit(c))
	{
		s=(s<<1)+(s<<3)+c-'0';
		c=getchar();
	}
	return s*p;
}
int Cag(int x,int y)
{
	return (x-1)*m+y;
}
void add(int x,int y,int cap)
{
	b[++t]=node(y,cap);
	b[t].nxt=head[x];
	head[x]=t;
	b[++t]=node(x,0);
	b[t].nxt=head[y];
	head[y]=t;
	return;
}
void Add(int x,int y)
{
	int Tx,Ty;
	for(int i=0;i<4;i++)
	{
		Tx=x+Cx[i];Ty=y+Cy[i];
		if(p[Tx][Ty])//边框的用处
			add(Cag(x,y)+Max,T,INF);//在矩阵外就和汇点T相连
		else
			add(Cag(x,y)+Max,Cag(Tx,Ty),INF);//否则出点和入点相连
	}
	return;
}
bool BFS()
{
	int i,cur;
	int to,cap;
	queue<int>p;
	memset(deep,0,sizeof(deep));
	p.push(S);deep[S]=1;
	while(!p.empty())
	{
		cur=p.front();p.pop();
		for(i=head[cur];i;i=b[i].nxt)
		{
			to=b[i].to;cap=b[i].cap;
			if(cap&&!deep[to])
			{
				deep[to]=deep[cur]+1;
				p.push(to);
				if(to==T)
					return 1;
			}
		}
	}
	return 0;
}
int Dinic(int k,int flow)
{
	if(k==T)
		return flow;
	int i,to,cap,res,rest=flow;
	for(i=head[k];i&&rest;i=b[i].nxt)
	{
		to=b[i].to;cap=b[i].cap;
		if(cap&&deep[to]==deep[k]+1)
		{
			res=Dinic(to,min(rest,cap));
			if(!res)
				deep[to]=0;
			b[i].cap-=res;
			b[i^1].cap+=res;
			rest-=res;
		}
	}
	return flow-rest;
}
int main()
{
	int i,j,flow;
	n=read();m=read();
	Max=n*m;T=Max+Max+1;//汇点T
	for(i=1;i<=n;i++)//裱个框,方便判断
		p[i][0]=p[i][m+1]=1;
	for(i=1;i<=m;i++)
		p[0][i]=p[n+1][i]=1;
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
			a[i][j]=read();
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		{
			if(a[i][j]==-1)//如果是障碍就不建边
				continue;
			if(a[i][j]==0)//如果是大兵就与源点建边,注意,是出点!否则都要为zero了……
				add(S,Cag(i,j)+Max,INF);
			else//如果是空地,那么在自己的入点和出点之间建边
				add(Cag(i,j),Cag(i,j)+Max,a[i][j]);//注意,这里的容量是a[i][j],就是要炸多少次
			Add(i,j);//放个函数里看看能不能和四周相连
		}
	while(BFS())//Dinic模板
		while((flow=Dinic(S,INF)))
			Maxflow+=flow;
	printf("%d",Maxflow);
	return 0;
}

推荐题目

Luogu P2472 [SCOI2007]蜥蜴

有点类似,但很不相同!

posted @ 2019-01-02 14:29  h^ovny  阅读(152)  评论(0编辑  收藏  举报