zoukankan      html  css  js  c++  java
  • [网络流]BZOJ4657 tower 最小割约束

    题面:

    Description
    Nick最近在玩一款很好玩的游戏,游戏规则是这样的:
    有一个n*m的地图,地图上的每一个位置要么是空地,要么是炮塔,要么是一些BETA狗,Nick需
    要操纵炮塔攻击BETA狗们。
    攻击方法是:对于每个炮塔,游戏系统已经给出它可以瞄准的方向(上下左右其中一个),Nick需要
    选择它的攻击位置,每一个炮塔只能够攻击一个位置,炮塔只能够向着它的瞄准方向上的某个位置发
    动攻击,当然炮塔也可以不进行攻击。炮塔威力强大,它可以且仅可以消灭目标位置上所有的BETA狗。
    出于安全考虑,游戏系统已经保证不存在一个炮塔能够瞄准另外一个炮塔,即对于任意一个炮
    塔,它所有可能的攻击位置上不存在另外一个炮塔。而且,如果把炮塔的起点和终点称为炮弹的运行
    轨迹,那么系统不允许两条轨迹相交(包括起点和终点)。
    现在,选定目标位置以后,每一个炮塔同时开炮,你要告诉Nick,他最多可以干掉多少BETA狗。
    Input
    第一行两个正整数n,m,表示地图的规模。
    接下来礼行,每行m个整数,0表示空地,-1,-2,一3,-4分别表示瞄准上下左右的炮塔,若为正整
    数p,则表示该位置有p个BETA狗。
    n,m <= 50,每个位置的BETA狗数量不超过999个,保证不存在任意一个炮塔能够瞄准另外一个炮塔
    Output
    一个正整数,表示Nick最多可以干掉几个BETA狗
    Sample Input
    3 2
    0 9
    -4 3
    0 -1
    Sample Output
    9
    分析:

    显然,题目保证了只有横炮台与纵炮台才可能会有相交的攻击轨迹。

    然后对于每对有相交攻击范围的横炮台与纵炮台,它们对答案的贡献得分不仅决定于它们自己的攻击范围内的BETA狗,还会被其它的炮台所局限。所以,每个炮台对于进攻目标的选择,正好需要最大流最小割的类似自我调整的性质。

    考虑如何连边。如果从最大流的角度思考,难以表达“炮台A选了BEAT狗a,炮台B就不能选a”这种逻辑关系,所以改换最小割。若一对纵横炮台A与B存在公共轨道,可以看成A到B有一条路径,就像这样:

    那么我们割就是让两个炮台无法相交。

    如果两个炮台要攻击最大值时都要去跨过它们攻击轨道的相交点,那么我们的割就要让其中一个炮台无法越过相交点,另一个不再受那个炮台的约束。

    我们可以把所有点拿出来当一个部,每个炮台的轨道上的点依次连边。

    若该轨道为纵向,则轨道上的第一个点与原点连边,方向与炮台方向相同,同理连第一个点与第二个点,方向与炮台相同(因为打到一个点时,它之前的点会被轨道覆盖,所以不能分别与原点连边),以此类推。若该轨道为横向,则第一个点与汇点连边,方向与炮台方向相反,以此类推。

    又因为两个炮台之间的路径只能有一次拐弯,故拆点,左边的点表示纵向轨迹,右边的点表示横向轨迹,相同的点之间从左到右连一条边。

    这样就可以表示出全部的纵炮到横炮的路径(纵横可以反过来)。

    接下来考虑一下割边的代价。

    我们先来考虑割边的意义。割某条边就代表有一个炮台打到的点只能是那个边的一个端点,而另外一个的炮台就不再受这个炮台的限制。

    所以,首先满足的是割的边必须是同一条轨道上相邻点之间的边,所以 相同点之间的边 和 点与汇点,源点的边 的代价(即容量)为inf。这样它们就不会被割了。

    对于其它边的讨论,容我摘一段xmy大佬的解释:

    把炮塔能够打到的beta狗中的最大值记为mx,那么炮塔路径最多只会延伸到第一个mx,再往后只会更劣。我们先把每个炮塔的mx加入ans中。

    对于竖炮塔的一条边u->v,割掉它表示炮塔打到了u点,那么ans就会损失掉mx-u点的beta狗的值,于是这条边的容量为mx-u点的beta狗。

    对于横炮塔的一条边u->v,割掉它表示炮塔打到了v点,那么ans就会损失掉mx-v点的beta狗的值于是这条边的容量为mx-v点的beta狗。

    所以答案就是ans-最小割。

     后附上代码

     

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define p(i,j) (i-1)*m+j
    using namespace std;
    const int maxp=5005,inf=0x3f3f3f3f;
    int n,m,tot,map[55][55], dx[4]={-1,1,0,0},dy[4]={0,0,-1,1},d[maxp],vd[maxp];
    int S,T,ans,cnt=1,sum,info[maxp];
    struct flow{int u,v,nex,w;}e[30005];
    int sap(int i,int augco)
    {
        if(i==T)return augco;
        int j,tmp,ret=0,mind=T-1;
        for(int o=info[i];o;o=e[o].nex)
        {
            j=e[o].v;
            if(e[o].w>0){
                if(d[i]==d[j]+1)
                {
                    tmp=sap(j,min(e[o].w,augco));
                    e[o].w-=tmp;
                    e[o^1].w+=tmp;
                    ret+=tmp;
                    augco-=tmp;
                    if(d[1]>=T)return ret;
                    if(augco==0)break;
                }
                mind=min(mind,d[j]);
            }
        }
        if(ret==0)
        {
            vd[d[i]]--;
            if(vd[d[i]]==0)
                d[1]=T;
            d[i]=mind+1;
            vd[d[i]]++;
        }
        return ret;
    }
    void solve()
    {
        memset(vd,0,sizeof vd);
        memset(d,0,sizeof d);
        vd[0]=T;ans=0;
        while(d[S]<T)
            ans+=sap(1,inf);
    }
    void add(int u,int v,int w)
    {
        //printf("%d %d %d
    ",u,v,w);
        e[++cnt]=(flow){u,v,info[u],w};info[u]=cnt;
        e[++cnt]=(flow){v,u,info[v],0};info[v]=cnt;
    }
    int main()
    {
        scanf("%d%d",&n,&m);S=1;T=1+n*m+n*m+1;
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&map[i][j]);
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(map[i][j]<0)
        {
            int k=-map[i][j]-1,mx=0,x,y;
            map[i][j]=0;
            for(x=i+dx[k],y=j+dy[k];1<=x&&x<=n&&1<=y&&y<=m;x+=dx[k],y+=dy[k])mx=max(mx,map[x][y]);
            sum+=mx;
            if(k==0||k==1)
            {
                add(S,1+p(i,j),inf);
                for(x=i,y=j;map[x][y]!=mx;x+=dx[k],y+=dy[k])
                    add(1+p(x,y),1+p(x+dx[k],y+dy[k]),mx-map[x][y]);
            }
            if(k==2||k==3)
            {
                add(1+n*m+p(i,j),T,inf);
                for(x=i,y=j;map[x][y]!=mx;x+=dx[k],y+=dy[k])
                    add(1+n*m+p(x+dx[k],y+dy[k]),1+n*m+p(x,y),mx-map[x][y]);
            }
        }
        for(int i=1;i<=n*m;i++) add(1+i,1+n*m+i,inf);solve();
        printf("%d
    ",sum-ans);
    }
  • 相关阅读:
    5.9
    5.8
    5.4
    04--深入探讨C++中的引用
    01--Qt扫盲篇
    00--Qt Creator 你必须要掌握的快捷操作
    02--读书笔记之:C++ Primer (第4版)及习题
    01--[转]C++强大背后
    01--数据结构——动态链表(C++)
    04-手把手教你把Vim改装成一个IDE编程环境(图文)
  • 原文地址:https://www.cnblogs.com/firecrazy/p/10785502.html
Copyright © 2011-2022 走看看