zoukankan      html  css  js  c++  java
  • 「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]蜥蜴

    有点类似,但很不相同!

  • 相关阅读:
    406. 根据身高重建队列 vector 自定义排序
    5552. 到家的最少跳跃次数 BFS
    5602. 将 x 减到 0 的最小操作数 双指针
    Hadoop Container is running beyond memory limits code143
    1122. 数组的相对排序 自定义排序
    328. 奇偶链表 链表
    127. 单词接龙 bfs
    5600. 第 K 条最小指令 组合数学
    5548. 最小体力消耗路径 二分
    1024. 视频拼接 dp
  • 原文地址:https://www.cnblogs.com/hovny/p/10208384.html
Copyright © 2011-2022 走看看