• uva 12264 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 Input

      3
      110
      NYN
      YNY
      NYN
      7 7332005 NYNNNNN YNYYNNN NYNYYNN NYYNYNN NNYYNNN NNNNNNY NNNNNYN

      Sample Output

     
    题意:给n个点的无权无向图(n<=100),每个点表示一个领地,且每个点有一个非负数ai权值。若ai==0则此点归敌方所有,若ai>0则此点归你且上面有ai个属于你的士兵。保证至少有一个属于你的点与敌方的点相邻。你可以让你的每个士兵最多向相邻领地移动一次,每次可以待在原地或者去到相邻的属于你的领地,但每个点至少要留1各士兵,使得最薄弱的关口尽量坚固。关口是指与敌方点相邻的点,薄弱与坚固分别指兵少与兵多。即使得每个和敌军相邻的自己领地至少有m个士兵,使得m尽可能大。
     
     
    题解:
    首先我们可以二分答案m。
    把每个点拆成两个点,一个入点,一个出点,入点向自己的出点的和每个相邻的点的出点连一条边,容量是ai,每个点出点连一条边到汇点,若不与敌人领地相邻,则容量为1,否则,容量是m,我们只需要二分这个值,跑一下网络流,如果满流,表示可以,否则不行。

    Q:为何需要拆点?

    A:我们希望通过拆点来实现每个士兵最多移动一次。下图中每个INF的边都是士兵移动的边,观察后可以发现如此建图士兵只能从一个点的入点移动到另一个点的出点,因此最多只能移动一次。(根据题意每次最远移动到相邻的点)

    Q:为何与敌军不相邻的点的出点需要连一条容量为1的边到汇点?

    A:为了保证己方的点至少有1个士兵。

    本篇通过第二个样例讲解思路,下图是第二个样例的建图结果。

    敌方的点不需要参与建图,因此图中没有6,7号点。

    INF指无穷,mid指二分的中值。

    注意7出->汇点容量应该为1.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<stack>
    using namespace std;
    #define rep(i,a,n) for (int i=a;i<n;i++)
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    #define pb push_back
    #define fi first
    #define se second
    typedef vector<int> VI;
    typedef long long ll;
    typedef pair<int,int> PII;
    const int inf=0x3fffffff;
    const ll mod=1000000007;
    
    const int MAXN=210;//点数的最大值
    const int MAXM=20110;//边数的最大值
    const int INF=0x3f3f3f3f;
    
    struct Node
    {
        int from,to,next;
        int cap;
    }edge[MAXM];
    int tol;
    int head[MAXN];
    int dep[MAXN];
    int gap[MAXN];//gap[x]=y :说明残留网络中dep[i]==x的个数为y
    
    int N;//N是总的点的个数,包括源点和汇点
    
    void init()
    {
        tol=0;
        memset(head,-1,sizeof(head));
    }
    
    
    void addedge(int u,int v,int w)
    {
        edge[tol].from=u;edge[tol].to=v;edge[tol].cap=w;edge[tol].next=head[u];
        head[u]=tol++;
        edge[tol].from=v;edge[tol].to=u;edge[tol].cap=0;edge[tol].next=head[v];
        head[v]=tol++;
    }
    void BFS(int start,int end)
    {
        memset(dep,-1,sizeof(dep));
        memset(gap,0,sizeof(gap));
        gap[0]=1;
        int que[MAXN];
        int front,rear;
        front=rear=0;
        dep[end]=0;
        que[rear++]=end;
        while(front!=rear)
        {
            int u=que[front++];
            if(front==MAXN)front=0;
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].to;
                if(dep[v]!=-1)continue;
                que[rear++]=v;
                if(rear==MAXN)rear=0;
                dep[v]=dep[u]+1;
                ++gap[dep[v]];
            }
        }
    }
    int SAP(int start,int end)
    {
        int res=0;
        BFS(start,end);
        int cur[MAXN];
        int S[MAXN];
        int top=0;
        memcpy(cur,head,sizeof(head));
        int u=start;
        int i;
        
        
        while(dep[start]<N)
        {
            if(u==end)
            {
                int temp=inf;
                int inser;
                for(i=0;i<top;i++)
                    if(temp>edge[S[i]].cap)
                    {
                        temp=edge[S[i]].cap;
                        inser=i;
                    }
                for(i=0;i<top;i++)
                {
                    edge[S[i]].cap-=temp;
                    edge[S[i]^1].cap+=temp;
                }
                res+=temp;
                top=inser;
                u=edge[S[top]].from;
            }
            if(u!=end&&gap[dep[u]-1]==0)//出现断层,无增广路
                break;
            for(i=cur[u];i!=-1;i=edge[i].next)
                if(edge[i].cap!=0&&dep[u]==dep[edge[i].to]+1)
                    break;
            if(i!=-1)
            {
                cur[u]=i;
                S[top++]=i;
                u=edge[i].to;
            }
            else
            {
                int min=N;
                for(i=head[u];i!=-1;i=edge[i].next)
                {
                    if(edge[i].cap==0)continue;
                    if(min>dep[edge[i].to])
                    {
                        min=dep[edge[i].to];
                        cur[u]=i;
                    }
                }
                --gap[dep[u]];
                dep[u]=min+1;
                ++gap[dep[u]];
                if(u!=start)u=edge[S[--top]].from;
            }
        }
        return res;
    }
    
    
    char s[110][110];
    int v[110];
    int n;
    int cnt;
    
    bool check(int x)
    {
        init();
        int st=0,ed=n*2+1;
        N=ed+1;
        int ans=0;
        rep(i,1,n+1)
        {
            if(v[i])
            {
                bool f=false;
                addedge(st,i,v[i]);
                addedge(i,i+n,v[i]);
                rep(j,1,n+1)
                {
                    if(s[i][j]=='Y')
                    {
                        if(v[j]) addedge(i,j+n,inf);
                        else f=true;
                    }
                }
                if(f) addedge(i+n,ed,x),ans+=x;
                else addedge(i+n,ed,1),ans++;
            }
        }
        return ans==SAP(st,ed);
    }
    
    int main()
    {
        int cas;
        scanf("%d",&cas);
        while(cas--)
        {
            scanf("%d",&n);
            int l=1,r=0;
            rep(i,1,n+1)
            {
                scanf("%d",&v[i]);
                r+=v[i];
            }
            rep(i,1,n+1) scanf("%s",s[i]+1);
            int ans=-1;
            while(l<=r)
            {
                int m=(l+r)/2;
                if(check(m)) ans=m,l=m+1;
                else r=m-1;
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    Python configparser模块
    Python OS,shutil模块
    一、操作系统基础
    Python 序列化
    Python 验证码生产程序和进度条程序
    SaltStack 实践课程二 PHP+NGINX
    Android攻城狮数据存储之文件存储
    Android攻城狮数据存储之SQLite数据库简介
    Android攻城狮数据存储之SharedPreferences
    Android攻城狮读取了JSON实现图文混排
  • 原文地址:https://www.cnblogs.com/tarjan/p/7276992.html
Copyright © 2020-2023  润新知