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 NNNNNYNSample Output
4
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; }