给n个点的无权无向图(n<=100),每个点有一个非负数ai。若ai==0则此点归敌方所有,若ai>0则此点归你且上面有ai个属于你的士兵。保证至少有一个属于你的点与敌方的点相邻。你可以让你的每个士兵最多移动一次,每次可以待在原地或者去到相邻的属于你的领地,但每个点至少要留1各士兵,使得最薄弱的关口尽量坚固。关口是指与敌方点相邻的点,薄弱与坚固分别指兵少与兵多。
我参考了这篇博客上的一些讲解。http://www.voidcn.com/blog/a197p/article/p-4252874.html
思路:把每个点拆成两个点,一个入度,一个出度,入度向自己的和每个相邻的点的出度连一条边,容量是ai,每个点出度连一条边到汇点,容量为1,那些与敌人相邻的点再多连一条边到汇点,容量是二分的值,我们只需要二分这个值,跑一下网络流,如果满流,表示可以,否则不行。
本篇通过第二个样例讲解思路,下图是第二个样例的建图结果。
敌方的点不需要参与建图,因此图中没有6,7号点。
INF指无穷,mid指二分的中值。
Q:为何需要拆点?
A:我们希望通过拆点来实现每个士兵最多移动一次。下图中每个INF的边都是士兵移动的边,观察后可以发现如此建图士兵只能从入点移动到另一个点的出点,因此最多只能移动一次。(根据题意每次最远移动到相邻的点)
Q:为何出点需要连一条容量为1的边到汇点?
A:为了保证己方的点至少有1个士兵,观察下图 1出 与 2出 满流时说明1和2点至少1个人。
Q:为何要二分?
A:我们要让最薄弱的关口尽量坚固,就得使关口的士兵分配得尽量均匀,然而最大流的算法不能保证分配均匀,我们只好一个个的试,满载就放宽条件,不满载就严加条件,直到试出可行的最大解。
以下渣代码
#include<stdio.h>
#include<vector>
#include<queue>
#include<string.h>
#define maxn 250
#define INF 0X3F3F3F3F
using namespace std;
int N;
int A[maxn];
char MAP[maxn][maxn];
bool ib[maxn];
struct Edge
{
int from,to,cap,flow;
Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct EdmondsKarp
{
int n,m;
vector<Edge>edges;
vector<int>G[maxn];
int a[maxn];
int p[maxn];
void init(int n)
{
this->n=n;
for(int i=0;i<n;i++) G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap)
{
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int Maxflow(int s,int t)
{
int flow=0;
for(;;)
{
memset(a,0,sizeof(a));
queue<int>Q;
Q.push(s);
a[s]=INF;
while(!Q.empty())
{
int x=Q.front();Q.pop();
for(unsigned int i=0;i<G[x].size();i++)
{
Edge& e=edges[G[x][i]];
if(!a[e.to]&&e.cap>e.flow)
{
p[e.to]=G[x][i];
a[e.to]=min(a[x],e.cap-e.flow);
Q.push(e.to);
}
}
if(a[t]) break;
}
if(!a[t]) break;
for(int u=t;u!=s;u=edges[p[u]].from)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
}
flow+=a[t];
}
return flow;
}
int Build(int val)
{
int ans=0;
memset(ib,0,sizeof(ib));
init(2*N+2);
for(int i=1;i<=N;i++)
{
if(!A[i]) continue;
AddEdge(0,i,A[i]);
AddEdge(i,i+N,A[i]);
for(int j=1;j<=N;j++)
if(MAP[i][j]=='Y')
{
if(!A[j]) ib[i]=true;
else AddEdge(i,j+N,INF);
}
}
for(int i=1;i<=N;i++)
if(ib[i]) {AddEdge(i+N,n-1,val);ans+=val;}
else if(A[i]){AddEdge(i+N,n-1,1);ans++;};
return ans;
}
void solve()
{
int a,b,ans;
int l=0,r=10010;
while(l<r)
{
int mid=(l+r)>>1;
a=Build(mid);
b=Maxflow(0,n-1);
if(a==b)
{
l=mid+1;
ans=mid;
}
else r=mid;
}
printf("%d\n",ans);
}
};
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&N);
for(int i=1;i<=N;i++) scanf("%d",&A[i]);
for(int i=1;i<=N;i++) scanf("%s",MAP[i]+1);
EdmondsKarp EK;
EK.solve();
}
return 0;
}