【XSY2925】cti 网络流

题目描述

  有一个 \(n\times m\)的网格,每个格子里面可能有一些炮塔,或者有几个人。

  每个炮塔可以在给定的方向(上下左右)上选一个点作为它的攻击位置,然后消灭这个格子里面的所有人。当然也可以不进行攻击。

  要求两个炮弹的飞行轨迹不能相交。

  问你最多能打死多少个人。

  保证不存在一个炮塔可以攻击另一个炮塔的情况。

  \(n,m\leq 50,\) 每个格子的人数 \(< 1000\)

题解

  先忽略"要求两个炮弹的飞行轨迹不能相交"这个条件。

  那么可以把每个炮塔能攻击到的格子找出来,连成一条链,容量为前面这个格子的人数。

  但这样求出来的是最小值。

  用一个大整数去减掉每个数作为边权就好了。

  现在有"要求两个炮弹的飞行轨迹不能相交"这个条件。

  参考 HNOI2013这道题。

  可以把朝向为左右的炮塔的链的方向反过来,然后在相交的位置上连一条(竖着的炮塔那一列对应的格子 \(\to\) 横着的炮塔那一行对应的格子)的边,容量为 \(\infty\)

  为什么这是对的?

  1097689-20180522190752495-1475559324.png

  如果竖着的炮塔攻击的点超过了两条链的交点,横着的炮塔攻击的点也超过了两条链的交点,那么那条容量为 \(\infty\) 的边就一定会有流量经过,然后会沿着 \(S\to\) 交点 \(\to T\) 流到终点。所以这样建图就保证了如果竖着的炮塔攻击的点超过了两条链的交点,那么横着的炮塔攻击的点就不会超过了两条链的交点。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
    if(a>b)
        swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
int rd()
{
    int s=0,c,b=0;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c=='-')
    {
        c=getchar();
        b=1;
    }
    do
    {
        s=s*10+c-'0';
    }
    while((c=getchar())>='0'&&c<='9');
    return b?-s:s;
}
void put(int x)
{
    if(!x)
    {
        putchar('0');
        return;
    }
    static int c[20];
    int t=0;
    while(x)
    {
        c[++t]=x%10;
        x/=10;
    }
    while(t)
        putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
    if(b<a)
    {
        a=b;
        return 1;
    }
    return 0;
}
int upmax(int &a,int b)
{
    if(b>a)
    {
        a=b;
        return 1;
    }
    return 0;
}
const int inf=0x7fffffff;
namespace flow
{
    int v[100010];
    int c[100010];
    int t[100010];
    int h[100010];
    int cnt;
    void add(int x,int y,int a)
    {
        cnt++;
        v[cnt]=y;
        c[cnt]=a;
        t[cnt]=h[x];
        h[x]=cnt;
    }
    int e[100010];
    int d[100010];
    int op(int x)
    {
        return ((x-1)^1)+1;
    }
    int S,T,num;
    queue<int> q;
    void bfs()
    {
        memset(d,-1,sizeof d);
        q.push(T);
        d[T]=0;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            e[d[x]]++;
            for(int i=h[x];i;i=t[i])
                if(c[op(i)]&&d[v[i]]==-1)
                {
                    d[v[i]]=d[x]+1;
                    q.push(v[i]);
                }
        }
    }
    int cur[100010];
    int dfs(int x,int flow)
    {
        if(x==T)
            return flow;
        int s=0;
        for(int &i=cur[x];i;i=t[i])
            if(c[i]&&d[v[i]]==d[x]-1)
            {
                int u=dfs(v[i],min(flow,c[i]));
                c[i]-=u;
                c[op(i)]+=u;
                flow-=u;
                s+=u;
                if(!flow)
                    return s;
            }
        e[d[x]]--;
        if(!e[d[x]])
            e[S]=num;
        d[x]++;
        e[d[x]]++;
        cur[x]=h[x];
        return s;
    }
    int solve()
    {
        bfs();
        int ans=0;
        memcpy(cur,h,sizeof h);
        while(d[S]>=0&&d[S]<=num-1)
            ans+=dfs(S,inf);
        return ans;
    }
}
void add(int x,int y,int z)
{
    flow::add(x,y,z);
    flow::add(y,x,0);
}
int n,m;
int a[100][100];
int b[100][100];
int main()
{
    open("c");
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    flow::S=1;
    flow::T=2;
    flow::num=2;
    int cnt=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(a[i][j]==-3)
            {
                if(j==1)
                    continue;
                cnt++;
                for(int k=j;k>=1;k--)
                    b[i][k]=++flow::num;
                add(flow::S,b[i][1],1000-a[i][1]);
                for(int k=1;k<j;k++)
                    add(b[i][k],b[i][k+1],1000-a[i][k+1]);
                add(b[i][j],flow::T,1000);
            }
            else if(a[i][j]==-4)
            {
                if(j==m)
                    continue;
                cnt++;
                for(int k=j;k<=m;k++)
                    b[i][k]=++flow::num;
                add(flow::S,b[i][m],1000-a[i][m]);
                for(int k=j;k<m;k++)
                    add(b[i][k+1],b[i][k],1000-a[i][k]);
                add(b[i][j],flow::T,1000);
            }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(a[i][j]==-1)
            {
                if(i==1)
                    continue;
                cnt++;
                for(int k=1;k<=i;k++)
                {
                    int v=++flow::num;
                    if(b[k][j])
                        add(v,b[k][j],inf);
                    b[k][j]=v;
                }
                add(flow::S,b[i][j],1000);
                add(b[i][j],b[i-1][j],1000);
                for(int k=i-1;k>1;k--)
                    add(b[k][j],b[k-1][j],1000-a[k][j]);
                add(b[1][j],flow::T,1000-a[1][j]);
            }
            else if(a[i][j]==-2)
            {
                if(i==n)
                    continue;
                cnt++;
                for(int k=i;k<=n;k++)
                {
                    int v=++flow::num;
                    if(b[k][j])
                        add(v,b[k][j],inf);
                    b[k][j]=v;
                }
                add(flow::S,b[i][j],1000);
                add(b[i][j],b[i+1][j],1000);
                for(int k=i+1;k<n;k++)
                    add(b[k][j],b[k+1][j],1000-a[k][j]);
                add(b[n][j],flow::T,1000-a[n][j]);
            }
    int ans=flow::solve();
    ans=cnt*1000-ans;
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/ywwyww/p/9073592.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值