zoukankan      html  css  js  c++  java
  • 【[NOI2009]植物大战僵尸】

    题目

    我太(zz)

    有一个非常显然的问题就是一个植物显然能保护同一行上比它更靠后的植物,因为显然得先干掉更靠前的植物

    首先可以看出来这是一个经典的最大权闭合子图的模型,于是去套最小割

    发现植物的收益有正有负,于是想到正的和源点连边,负的和汇点连边

    我们再来考虑一个植物没有被割掉的的状态

    显然是和源点的那条边没被割掉,所以这个植物应该向保护它的植物连边,连代价为(inf)的边,想要保留和源点的边就必须去割掉这些连出去的边指向的植物到汇点的边

    但是发现这样连样例都过不了

    因为有一些植物非常神仙,自己保护自己,还有一些植物被神仙的植物保护,还有一些植物互相保护成了一个环

    这些无敌的植物根本干不掉,于是不能算到最大权闭合子图里

    于是我们要搞一个拓扑排序,把环里的点都给干掉

    之后连边最小割就好了,记得把重边去掉

    代码

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #define maxn 605
    #define re register
    #define LL long long
    #define inf 999999999
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    struct E{int v,nxt,f,w;}e[maxn*maxn*2];
    int n,m,S,T,num=1,ans,tot;
    int head[maxn],d[maxn],cur[maxn];
    int q[maxn],r[maxn],map[maxn][maxn];
    std::vector<int> v[maxn];
    std::vector<int> x[21][31],y[21][31];
    int val[21][31],to[21][31];
    inline void add(int x,int y,int z) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].w=z;}
    inline void C(int x,int y,int z) {add(x,y,z),add(y,x,0);}
    inline char gc()
    {
        static char buff[1000000],*S=buff,*T=buff;
        return S==T&&(T=(S=buff)+fread(buff,1,1000000,stdin),S==T)?EOF:*S++;
    }
    inline int read()
    {
    	char c=gc();int x=0,r=1;while(c<'0'||c>'9') {if(c=='-') r=-1;c=gc();}
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=gc();
    	return x*r;
    }
    inline int BFS()
    {
    	std::queue<int> q;
    	for(re int i=S;i<=T;i++) d[i]=0,cur[i]=head[i];
    	d[S]=1,q.push(S);
    	while(!q.empty())
    	{
    		int k=q.front();q.pop();
    		for(re int i=head[k];i;i=e[i].nxt)
    		if(!d[e[i].v]&&e[i].w>e[i].f) d[e[i].v]=d[k]+1,q.push(e[i].v);
    	}
    	return d[T];
    }
    int dfs(int x,int now)
    {
        if(x==T||!now) return now;
        int ff,flow=0;
        for(re int& i=cur[x];i;i=e[i].nxt)
        if(d[e[i].v]==d[x]+1)
        {
            ff=dfs(e[i].v,min(now,e[i].w-e[i].f));
            if(ff<=0) continue;
            flow+=ff,now-=ff;
            e[i].f+=ff,e[i^1].f-=ff;
            if(!now) break;
        }
        return flow;
    }
    int main()
    {
    	n=read(),m=read();
    	for(re int i=1;i<=n;i++)
    		for(re int j=1;j<=m;j++)
    		{
    			val[i][j]=read();
    			to[i][j]=(i-1)*m+j;
    			if(j>1) v[to[i][j]].push_back(to[i][j-1]),r[to[i][j-1]]++;
    			int t=read();int X,Y;
    			for(re int k=0;k<t;k++) X=read()+1,Y=read()+1,x[i][j].push_back(X),y[i][j].push_back(Y);
    		}
    	for(re int i=1;i<=n;i++)
    		for(re int j=1;j<=m;j++)
    			for(re int k=0;k<x[i][j].size();k++)
    			{
    				int xx=x[i][j][k],yy=y[i][j][k];
    				for(re int t=1;t<=yy;t++) r[to[xx][t]]++,v[to[i][j]].push_back(to[xx][t]);
    			}
    	for(re int i=1;i<=n;i++)	
    		for(re int j=1;j<=m;j++)
    			if(!r[to[i][j]]) q[++tot]=to[i][j];
    	for(re int i=1;i<=tot;i++)
    		for(re int j=0;j<v[q[i]].size();j++)
    		{
    			r[v[q[i]][j]]--;
    			if(!r[v[q[i]][j]]) q[++tot]=v[q[i]][j];
    		}
    	S=0,T=n*m+1;
    	for(re int i=1;i<=n;i++)
    		for(re int j=1;j<=m;j++)
    		{
    			if(r[to[i][j]]) continue;
    			if(val[i][j]<0) C(to[i][j],T,-1*val[i][j]);
    				else C(S,to[i][j],val[i][j]),ans+=val[i][j];
    			for(re int k=0;k<v[to[i][j]].size();k++) 
    				if(!r[v[to[i][j]][k]]&&!map[v[to[i][j]][k]][to[i][j]]) 
    					map[v[to[i][j]][k]][to[i][j]]=1,C(v[to[i][j]][k],to[i][j],inf);
    		}
    	while(BFS()) ans-=dfs(S,inf);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    数组指针和指针数组的区别
    C++虚函数
    C++容器
    红黑树
    COM RTS/CTS, DTR/DSR
    linux和windows多线程的异同
    socket
    C++vector使用
    select函数详解
    linux下头文件
  • 原文地址:https://www.cnblogs.com/asuldb/p/10289048.html
Copyright © 2011-2022 走看看