网络流 UVA 12264 Risk

F  Risk

Risk is a board game played on a world map. This world is divided into regions by borders. Each region is controlled by a player (either you or one of your opponents). Any region that you control contains a positive number of your armies.
In each turn, you are allowed to move your armies. Each of your armies may either stay where it is, or move from a region to a bordering region under your control. The movements are performed one by one, in an order of your choice. At all times, each region must contain at least one army.
For strategic purposes, it is important to move your armies to regions that border your opponents' regions and minimize the number of armies on your regions that are totally surrounded by other regions under your control. You want your weakest link, i.e., the border region with the minimum number of armies, to be as strong as possible. What is the maximum number of armies that can be placed on it after one turn?

Input

On the first line a positive integer: the number of test cases, at most 100. After that per test case:
  • One line with an integer n (1 ≤ n ≤ 100): the number of regions.
  • One line with n integers ai (0 ≤ ai ≤ 100): the number of your armies on each region. A number 0 indicates that a region is controlled by your opponents, while a positive number indicates that it is under your control.
  • n lines with n characters, where each character is either `Y' or `N'. The i-th character of the j-th line is `Y' if regions i and j border, and `N' otherwise. This relationship is symmetric and the i-th character of the i-th line will always be `N'.
In every test case, you control at least one region, and your opponents control at least one region. Furthermore, at least one of your regions borders at least one of your opponents' regions.

Output

Per test case:
  • One line with an integer: the maximum number of armies on your weakest border region after one turn of moving.

Sample in- and output

InputOutput
2
3
1 1 0
NYN
YNY
NYN
7
7 3 3 2 0 0 5
NYNNNNN
YNYYNNN
NYNYYNN
NYYNYNN
NNYYNNN
NNNNNNY
NNNNNYN
1
4


思路:把每个点拆成两个点,一个入度,一个出度,入度向自己的和每个相邻的点的出度连一条边,容量是ai,每个点出度连一条边到汇点,容量为1,那些与敌人相邻的点再多连一条边到汇点,容量是二分的值,我们只需要二分这个值,跑一下网络流,如果满流,表示可以,否则不行。


代码:

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <string.h>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define LL long long
using namespace std;
#define eps 1e-7
const int maxn=200+5;
const int inf=1e8;
struct Edge
{
    int u,v,cap,flow;
    Edge(int u,int v,int cap,int flow)
    :u(u),v(v),cap(cap),flow(flow) { }
};

vector<Edge> edges;
vector<int> G[maxn];

void add(int u,int v,int cap)
{
    edges.push_back(Edge(u,v,cap,0));
    edges.push_back(Edge(v,u,0,0));
    int m=edges.size();
    G[u].push_back(m-2);
    G[v].push_back(m-1);
}

struct ISAP
{
    int d[maxn], p[maxn], num[maxn], cur[maxn];
    int n, s, t;
    void init(int n) { this->n = n; }


    int Augment()
    {
        int x = t, a = inf;
        while (x != s)
        {
            Edge&e = edges[p[x]];
            a = min(a, e.cap - e.flow);
            x = e.u;
        }
        x = t;
        while (x != s)
        {
            edges[p[x]].flow += a;
            edges[p[x] ^ 1].flow -= a;
            x = edges[p[x]].u;
        }
        return a;
    }


    void bfs()
    {
        for (int i = 0; i<n; ++i) d[i] = inf;
        queue<int> q;
        d[t] = 0;
        q.push(t);
        while (q.size())
        {
            int x = q.front(); q.pop();
            for (int i = 0; i<G[x].size(); ++i)
            {
                Edge&e = edges[G[x][i]];
                if (e.cap>0 || d[e.v] <= d[x] + 1) continue;
                d[e.v] = d[x] + 1;
                q.push(e.v);
            }
        }
    }


    int maxflow(int s, int t)
    {
        this->s = s, this->t = t;
        memset(num, 0, sizeof(num));
        memset(cur, 0, sizeof(cur));
        bfs();
        for (int i = 0; i<n; ++i)
        if (d[i] != inf) ++num[d[i]];
        int flow = 0, x = s;
        while (d[s]<n)
        {
            if (x == t)
            {
                flow += Augment();
                x = s;
            }
            int ok = 0;
            for (int i = cur[x]; i<G[x].size(); ++i)
            {
                Edge&e = edges[G[x][i]];
                if (e.cap>e.flow&&d[e.v] + 1 == d[x])
                {
                    ok = 1;
                    cur[x] = i;
                    p[e.v] = G[x][i];
                    x = e.v;
                    break;
                }
            }
            if (!ok)
            {
                int m = n - 1;
                for (int i = 0; i<G[x].size(); ++i)
                {
                    Edge&e = edges[G[x][i]];
                    if (e.cap>e.flow) m = min(m, d[e.v]);
                }
                if (--num[d[x]] == 0) break;
                ++num[d[x] = m + 1];
                cur[x] = 0;
                if (x != s) x = edges[p[x]].u;
            }
        }
        return flow;
    }
}solver;

int n,s,t;
bool enm[maxn];
int a[maxn];
vector<int> bin;
int cnt,ctr;

inline int in(int x) { return (x<<1)-1; }
inline int out(int x) { return x<<1; }

void input()
{
    scanf("%d",&n);
    memset(enm,0,sizeof(enm));
    edges.clear();
    s=0; t=2*n+1; solver.init(t+1);
    for(int i=0;i<maxn;++i) G[i].clear();
    ctr=0;
    for(int i=1;i<=n;++i) {
        int x; scanf("%d",&x);
        a[i]=x;
        if(x==0) enm[i]=true;
        else { add(s,in(i),x); add(in(i),out(i),x); add(out(i),t,1); ctr++; }
    }
    char s[110];
    bin.clear(); cnt=0;
    for(int i=1;i<=n;++i) {
        scanf("%s",s+1);
        if(enm[i]) continue;
        bool near=false;
        for(int j=1;j<=n;++j) {
            if(s[j]=='N') continue;
            if(enm[j]) near=true;
            else add(in(i),out(j),a[i]);
        }
        if(near) { add(out(i),t,inf); int k=edges.size()-2; bin.push_back(k); ++cnt; }
    }
}

void clear(int up)
{
    for(int i=0;i<edges.size();++i) edges[i].flow=0;
    for(int i=0;i<bin.size();++i) {
        edges[bin[i]].cap=up;
    }
}

bool ok(int up)
{
    clear(up);
    int flow=solver.maxflow(s,t);
    return (up*cnt+ctr)==flow;
}

void solve()
{
    int l=1,r=n*100,m;
    int ans=0;
    while(l<=r) {
        m=(l+r)>>1;
        if(ok(m)) {
            ans=m;
            l=m+1;
        } else r=m-1;
    }
    ++ans; printf("%d\n",ans);
}

int main()
{
    int T;cin>>T;
    while(T--) {
        input();
        solve();
    }
}







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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值