• [2018.3.25集训]cti-最小割


    题目大意

    给定一个$n*m$的网格图上
    有些格子内存在炮台,每个炮台都被指定了上下左右四个方向中的某一个方向,并可以选定这个方向上的一个格子发动一次攻击。
    保证没有任何炮台能攻击另一个炮台,同时炮台可以不攻击。
    有些格子内存在数量为$a[i][j]$的敌人,攻击一个格子能击杀所有格子内的敌人。
    定义每个炮台与它攻击的目标之间的格子为攻击轨迹。一个合法的攻击方案满足,任意两个炮台之间的攻击轨迹不相交。
    求击杀敌人数最多的一种合法方案击杀的敌人数。

    $n,m leq 50,a[i][j] leq 1000$

    题解

    当你做一道题,发现什么都想不到的时候,可以试一试网络流。-——出题人

    考虑将格子上的权值取负,使用最小割模型。
    假设不考虑攻击轨迹不相交的限制,那么可以用如下方法建图:

    将每个格子拆成两个点,分别为横点和竖点。
    对于每个横着的炮台,使源点连向它,将其覆盖的格子的横点串成一条链,流量为每个格子的敌人数目,最后令链上最后一个边界格子连向汇点。
    对于每个竖着的炮台,使它连向汇点,同样将覆盖的格子串成一条链,但是使用的是竖点,且方向与横着时相反,并令源点向链的起点,也就是边界格子连边。
    这样,建出的图的一个割对应一组攻击方案。

    考虑不能相交的限制。

    考虑在对应相交位置处的横点向竖点连一条$Inf$边。
    此时,若横点割掉相交处的格子对应的边,则竖点割掉相交处格子及之前的边并不能使图不连通,因为横着的$Inf$边依旧可以流入。
    于是,竖点只能割掉之后的边。由于竖点的边是反着建的,因此剩余可选择的边集对应原图上不相交的所有方案。

    同理,若竖点割掉相交处的格子对应的边,则横点只能割掉相交处的格子对应的边之前的边,否则之间的边处的流量会通过$Inf$边流向竖点的汇点。

    这个模型的来源是HNOI的切糕一题。
    于是跑一边最小割即可~

    代码:
    (-1、-2、-3、-4分别对应上下左右)

    #include<bits/stdc++.h>
    using namespace std;
    
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
    	while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    	return x*f;
    }
    
    const int N=59;
    const int mint=1000;
    
    int n,m,s,t;
    int g[N][N],h[N][N];
    int dx[]={0,0,1,-1};
    int dy[]={1,-1,0,0};
    
    inline bool in(int x,int y)
    {
    	return 1<=x && x<=n && 1<=y && y<=m;
    }
    
    namespace flow
    {
    	const int P=N*N*5;
    	const int M=P*3;
    
    	int to[M<<1],nxt[M<<1],w[M<<1],beg[P],tot=1;
    	int dis[P],q[P];
    
        inline void adde(int u,int v,int c)
        {
            to[++tot]=v;
            nxt[tot]=beg[u];
            w[tot]=c;
            beg[u]=tot;
        }
    
        inline void add(int u,int v,int c)
       	{
    		//printf("%d %d %d
    ",u,v,mint-c);
            adde(u,v,c);adde(v,u,0);
        }
    
        inline bool bfs()
        {
            memset(dis,-1,sizeof(dis));
            q[1]=s;dis[s]=0;
            for(int l=1,r=1,u=q[l];l<=r;u=q[++l])
                for(int i=beg[u];i;i=nxt[i])
                    if(w[i]>0 && !(~dis[to[i]]))
                        dis[to[i]]=dis[u]+1,q[++r]=to[i];
            return ~dis[t];
        }
    
        inline int dfs(int u,int flow)
        {
            if(u==t || !flow)return flow;
            int cost=0;
            for(int i=beg[u],f;i;i=nxt[i])
                if(w[i]>0 && dis[to[i]]==dis[u]+1)
                {
                    f=dfs(to[i],min(flow-cost,w[i]));
                    w[i]-=f;w[i^1]+=f;cost+=f;
                    if(cost==flow)break;
                }
            if(!cost)dis[u]=-1;
            return cost;
        }
    
        inline int dinic()
        {
            int ret=0;
            while(bfs())
                ret+=dfs(s,2e9);
            return ret;
        }
    }
    
    inline int pt(int a,int b,int d)
    {
    	return (((a-1)*m+b)<<1)-d;
    }
    
    int main()
    {
    	freopen("cti.in","r",stdin);
    	freopen("cti.out","w",stdout);
    
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			g[i][j]=read();
    
    	int totcnt=0;
    	s=n*m*2+1;t=s+1;
    
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(g[i][j]<=-3)
    			{
    				flow::add(s,pt(i,j,1),mint);
    				int dir=g[i][j]+4,k;
    				for(k=1;in(i+k*dx[dir],j+k*dy[dir]);k++)
    				{
    					int cx=i+k*dx[dir],cy=j+k*dy[dir];
    					int lx=i+(k-1)*dx[dir],ly=j+(k-1)*dy[dir];
    					flow::add(pt(lx,ly,1),pt(cx,cy,1),mint-max(0,g[cx][cy]));
    					h[cx][cy]=dir+1;
    				}
    				k--;
    				int cx=i+k*dx[dir],cy=j+k*dy[dir];
    				flow::add(pt(cx,cy,1),t,1e8);
    				totcnt++;
    			}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(-2<=g[i][j] && g[i][j]<=-1)
    			{
    				flow::add(pt(i,j,0),t,mint);
    				int dir=g[i][j]+4,k;
    				for(k=1;in(i+k*dx[dir],j+k*dy[dir]);k++)
    				{
    					int cx=i+k*dx[dir],cy=j+k*dy[dir];
    					int lx=i+(k-1)*dx[dir],ly=j+(k-1)*dy[dir];
    					flow::add(pt(cx,cy,0),pt(lx,ly,0),mint-max(0,g[cx][cy]));
    					if(h[cx][cy])
    						flow::add(pt(cx-dx[h[cx][cy]-1],cy-dy[h[cx][cy]-1],1),pt(lx,ly,0),1e8);
    				}
    				k--;
    				int cx=i+k*dx[dir],cy=j+k*dy[dir];
    				flow::add(s,pt(cx,cy,0),1e8);
    				totcnt++;
    			}
    
    	printf("%d
    ",totcnt*mint-flow::dinic());
    	return 0;
    }
    
  • 相关阅读:
    C# 泛型的逆变与协变
    C# 元组
    DNS服务原理与搭建自己的DNS服务器
    浅析DNS域名解析过程
    Python turtle.circle()函数
    Python 实现点名系统
    PyCharm Debugger中Step Over、Step Into、Step Into My Code、Force Step Into、Step Out、Run to Cursor意思区别
    TypeScript与JavaScript比较(区别)
    微信小程序开发环境搭建
    Windows.edb 文件占据巨大的硬盘存储空间
  • 原文地址:https://www.cnblogs.com/zltttt/p/8654902.html
Copyright © 2020-2023  润新知