[TJOI2009]战争游戏 - 网络流,最小割

Posted nishikino-curtis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[TJOI2009]战争游戏 - 网络流,最小割相关的知识,希望对你有一定的参考价值。

Description

小R正在玩一个战争游戏。游戏地图是一个M行N列的矩阵,每个格子可能是障碍物,也可能是空地,在游戏开始时有若干支敌军分散在不同的空地格子中。每支敌军都可以从当前所在的格子移动到四个相邻的格子之一,但是不能移动到包含障碍物的格子。如果敌军移动出了地图的边界,那么战争就失败了。

现在你的任务是,在敌军开始移动前,通过飞机轰炸使得某些原本是空地的格子变得不可通行,这样就有可能阻止敌军移出地图边界(出于某种特殊的考虑,你不能直接轰炸敌军所在的格子)。由于地形不同的原因,把每个空地格子轰炸成不可通行所需的Bomb数目可能是不同的,你需要计算出要阻止敌军所需的最少的Bomb数。

Input & Output

Input

输入文件的第一行包含两个数M和N,分别表示矩阵的长和宽。接下来M行,每行包含用空格隔开的N个数字,每个数字表示一个格子的情况:若数字为-1,表示这个格子是障碍物;若数字为0,表示这个格子里有一支敌军;若数字为一个正数x,表示这个格子是空地,且把它轰炸成不可通行所需的Bomb数为x。

Output

输出一个数字,表示所需的最少Bomb数。数据保证有解存在。

Sample

Input

4 3
1 2 1
1 10 1
1 0 -1
1 1 1

Output

6

Solution

把每个可轰炸的点拆点,由i向i + ?连一条容量为点权的边,特别地,对于敌军所在的点,由源点向敌军,敌军向拆点各连一条INF,然后在所有非障碍的四连通格子间连双向INF,具体的操作是x -> y + ?, y -> x + ?,最后是所有非障碍边界向汇点连一条INF,这里说的边都包括了对应的反向弧。
求最小割即可。
Code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>

using std::memset;
using std::min;
using std::max;
using std::swap;
using std::cin;
using std::cout;
using std::endl;
using std::ios;
using std::queue;

const int maxn = 64;
const int INF = 0x3f3f3f3f;

struct edge
{
    int to,nxt,v;
}e[10005];
int n,m,s,t,lnk[10005],level[10005],ptr;
int mtr[32][32],num[32][32];

void add(int bgn,int end,int flow)
{
    e[ptr].v = flow; e[ptr].to = end; e[ptr].nxt = lnk[bgn]; lnk[bgn] = ptr; ++ptr;
    e[ptr].v = 0; e[ptr].to = bgn; e[ptr].nxt = lnk[end]; lnk[end] = ptr; ++ptr;
}
bool bfs()
{
    queue<int> q;
    memset(level,0,sizeof(level));
    level[s]=1; 
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int p=lnk[u]; ~p; p=e[p].nxt){
            int y=e[p].to;
            if(e[p].v > 0 && !level[y]){
                level[y]=level[u]+1;
                q.push(y);
            }
        }
    }
    if(level[t]==0) return false;
    else return true;
}
int dfs(int x,int fl)
{
    if(x==t)return fl;
    for(int p=lnk[x]; ~p; p=e[p].nxt){
        int y=e[p].to;
        if(level[y]==level[x]+1&&e[p].v!=0){
            int delta=dfs(y,min(fl,e[p].v));
            if(delta > 0){ 
                e[p].v-=delta;
                e[p^1].v+=delta;
                return delta;
            }
        }   
    }
    return 0;
}
int dinic()
{
    int ret=0;
    while(bfs()){
        while(int delta=dfs(s,INF))
            ret+=delta;
    }
    return ret;
}

int main()
{
    ios::sync_with_stdio(false);
    memset(lnk,-1,sizeof(lnk));
    cin >> n >> m;
    t = n*m; s = 0;
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
            cin >> mtr[i][j];   
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
        {
            num[i][j] = i * m + j;
            if(mtr[i][j])add(num[i][j],num[i][j] + t,mtr[i][j]);
        }   
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
        {
            if(mtr[i][j] == 0) add(s,num[i][j],INF) , add(num[i][j], num[i][j] + t, INF);
            if(i > 1 && mtr[i-1][j] >= 0 && mtr[i][j] >= 0)
                add(num[i][j] + t, num[i-1][j], INF),
                add(num[i-1][j] + t, num[i][j], INF);
            if(j > 1 && mtr[i][j-1] >= 0 && mtr[i][j] >= 0)
                add(num[i][j] + t, num[i][j-1], INF),
                add(num[i][j-1] + t, num[i][j], INF);
            if(i == 1 || i == n || j == 1 || j == m)
                add(num[i][j] + t, (t << 1) + 1, INF);
        }
    t = (t << 1) + 1;
    cout << dinic() << endl;
    return 0;
}

辣鸡博客园居然还有Bomb这种敏感词

以上是关于[TJOI2009]战争游戏 - 网络流,最小割的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj4554] [Tjoi2016&Heoi2016]游戏

[2016北京集训试题6]网络战争-[最小割树(网络流)+kd-tree+倍增]

bzoj1797: [Ahoi2009]Mincut 最小割(网络流,缩点)

[HEOI2016/TJOI2016]游戏

网络流中的最小路径覆盖问题

bzoj3175: [Tjoi2013]攻击装置&&4808: 马