【NOI2018模拟3.26】Cti

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011056504/article/details/79715324

Description

有一个 n × m 的地图, 地图上的每一个位置可以是空地, 炮塔或是敌人. 你需要操纵炮塔消灭敌人.
对于每个炮塔都有一个它可以瞄准的方向, 你需要在它的瞄准方向上确定一个它的攻击位置,当然也可以不进行攻击. 一旦一个位置被攻击, 则在这个位置上的所有敌人都会被消灭.
保证对于任意一个炮塔, 它所有可能的攻击位置上不存在另外一个炮塔.
定义炮弹的运行轨迹为炮弹的起点和终点覆盖的区域. 你需要求出一种方案, 使得没有两条炮弹轨迹相交.

Input

第一行两个整数 n,m.
接下来 n 行, 每行 m 个整数, 0 表示空地, −1,−2,−3,−4 分别表示瞄准上下左右的炮塔, 正整
数 p 表示表示此位置有 p 个敌人.

Output

一行一个整数表示答案.

Sample Input

输入1:
3 2
0 9
-4 3
0 -1

输入2:
4 5
0 0 -2 0 0
-4 0 5 4 0
0 -4 3 0 6
9 0 0 -1 0

Sample Output

输出1:
9

输出2:
12

Data Constraint

对于前 20% 的数据, n,m ≤ 5;
对于另 20% 的数据, 朝向上下的炮塔至多有 2 个;
对于另 20% 的数据, 至多有 6 个炮塔;
对于 100% 的数据, 1 ≤ n,m ≤ 50, 每个位置的敌人数量 < 1000.

Solution

一眼网络流。
然而这个的建模方法一前没弄过。
最小割模型。
对于横向射出的,依次连边连成一条链。
对于纵向的,依次反向连边练成一条链。
源点向横向的炮塔连正无穷,纵向炮塔向汇点连正无穷。
这样流都会从源点出发沿着边流到汇点。
而因为不能交叉,即不能存在这样的流,即割掉。
一开始把每个炮塔射程上的最大值统计进答案里,割表示选一个小一点的,即减掉一些,就是直接最小割就好了。
但是流从横的流到竖的后不能再流到横的那里。
所以分点就行了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100010
#define INF 214748347
using namespace std;
int S,T,n,m,last[N*10],nxt[N*10],to[N*10],data[N*10],tot=1,ans=0,d[N],bz[N],a[60][60];
void putin(int x,int y,int z)
{
    nxt[++tot]=last[x];last[x]=tot;to[tot]=y;data[tot]=z;
    nxt[++tot]=last[y];last[y]=tot;to[tot]=x;data[tot]=0;
}
bool bfs()
{
    int i=0,j=1;memset(bz,0,sizeof(bz));d[1]=S;bz[S]=1;
    while(i<j)
    {
        int x=d[++i];
        for(int k=last[x];k;k=nxt[k])
        if(bz[to[k]]==0&&data[k]>0) bz[to[k]]=bz[x]+1,d[++j]=to[k];
    }
    return bz[T]>0;
}
int dinic(int x,int v)
{
    if(x==T) return v;
    int ans=0;
    for(int i=last[x];i;i=nxt[i])
    {
        int y=to[i],qq=0;
        if(bz[y]==bz[x]+1&&data[i]>0)
        {
            qq=dinic(y,min(v,data[i]));
            if(qq) data[i]-=qq,ans+=qq,v-=qq,data[i^1]+=qq;
            if(v==0) return ans;
        }
    }
    if(ans==0) bz[x]=-1;
    return ans;
}
int p(int x,int y,int z)
{
    return (x-1)*m+y+n*m*z;
}
int main()
{
    freopen("cti.in","r",stdin);
    freopen("cti.out","w",stdout);
    scanf("%d%d",&n,&m);
    S=2*n*m+1;T=S+1;
    fo(i,1,n) fo(j,1,m) scanf("%d",&a[i][j]);
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            putin(p(i,j,0),p(i,j,1),INF);
            if(a[i][j]==-1)
            {
                int jy=0;
                fd(k,i-1,1) jy=max(jy,a[k][j]);
                ans+=jy;
                a[i][j]=0;
                fd(k,i-1,1)
                {
                    putin(p(k,j,1),p(k+1,j,1),jy-a[k+1][j]);
                }
                putin(p(i,j,1),T,INF);
            }
            if(a[i][j]==-2)
            {
                int jy=0;
                fo(k,i+1,n) jy=max(jy,a[k][j]);
                ans+=jy;
                a[i][j]=0;
                fo(k,i+1,n)
                {
                    putin(p(k,j,1),p(k-1,j,1),jy-a[k-1][j]);
                }
                putin(p(i,j,1),T,INF);
            }
            if(a[i][j]==-3)
            {
                int jy=0;
                fd(k,j-1,1) jy=max(jy,a[i][k]);
                ans+=jy;
                a[i][j]=0;
                fd(k,j-1,1)
                {
                    putin(p(i,k+1,0),p(i,k,0),jy-a[i][k+1]);
                }
                putin(S,p(i,j,0),INF);
            }
            if(a[i][j]==-4)
            {
                int jy=0;
                fo(k,j+1,m) jy=max(jy,a[i][k]);
                ans+=jy;
                a[i][j]=0;
                fo(k,j+1,m)
                {
                    putin(p(i,k-1,0),p(i,k,0),jy-a[i][k-1]);
                }
                putin(S,p(i,j,0),INF);
            }
        }
    }
    while(bfs()) ans-=dinic(S,INF);
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/u011056504/article/details/79715324