题意:
给n个点的无权无向图(n<=100),每个点有一个非负数ai。若ai==0则此点归敌方所有,若ai>0则此点归你且上面有ai个属于你的士兵。保证至少有一个属于你的点与敌方的点相邻。你可以让你的每个士兵最多移动一次,每次可以待在原地或者去到相邻的属于你的领地,但每个点至少要留1各士兵,使得最薄弱的关口尽量坚固。关口是指与敌方点相邻的点,薄弱与坚固分别指兵少与兵多。
思路:
讲的很好:https://blog.youkuaiyun.com/xl2015190026/article/details/51902823
代码:
// 最大流 + 二分
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 200+5;
int n, C[maxn], border_enemy[maxn];
char G2[maxn][maxn];
// 图
struct Edge{
int u,v,cap,flow;
Edge(int a, int b, int c, int d):u(a),v(b),cap(c),flow(d){}
};
vector<Edge> edges;
vector<int> G[maxn];
void init(int a){
for(int i = 0; i < a; ++i) G[i].clear();
edges.clear();
}
void addEdge(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);
}
int dis[maxn];
bool bfs(int s, int t){
memset(dis, -1, sizeof(dis));
queue<int> Q;
Q.push(s);
dis[s] = 0;
while(!Q.empty()){
int x = Q.front(); Q.pop();
for(int i = 0; i < G[x].size(); ++i){
Edge& e = edges[G[x][i]];
if(dis[e.v] == -1&&e.cap > e.flow){
dis[e.v] = dis[x] + 1;
Q.push(e.v);
}
}
}
return dis[t] != -1;
}
int dfs(int s, int t, int f){
if(s == t||f == 0) return f;
int ans = 0;
for(int i = 0; i < G[s].size(); ++i){
Edge& e = edges[G[s][i]];
if(dis[e.v] == dis[s] + 1&&e.cap > e.flow){
int a1 = min(f, e.cap - e.flow);
int w = dfs(e.v, t, a1);
if(0 == w) continue;
edges[G[s][i]].flow += w;
edges[G[s][i]^1].flow -= w;
ans += w;
f -= w;
if(f <= 0) break;
}
}
return ans;
}
int dinic(int s, int t){
int ans = 0;
while(bfs(s, t)){
//printf("hhh");
ans += dfs(s, t, INF);
}
return ans;
}
int build(int f, int s, int t){
memset(border_enemy, 0, sizeof(border_enemy));
init(2*n+2);
for(int i = 0; i < n; ++i){
if(0 == C[i]) continue; // 敌人的地盘
addEdge(s, i, C[i]);
addEdge(i, i+n, C[i]);
for(int j = 0; j < n; ++j){
if(G2[i][j] == 'Y'){
if(0 == C[j]) border_enemy[i] = 1;
else addEdge(i, j+n, INF);
}
}
}
// 统计到汇点的容量和
int full_flow = 0;
for(int i = 0; i < n; ++i){
if(border_enemy[i]){ addEdge(i+n, t, f); full_flow += f; }
else if(C[i]){ addEdge(i+n, t, 1); ++full_flow; }
}
return full_flow;
}
// 二分法
void solve(){
int s = 2*n, t = 2*n+1; // 最大流源点和汇点
int L = 0, R = 10000+10, ans = 0;
while(L < R){
int mid = (L+R)/2;
int full_flow = build(mid, s, t);
int flow = dinic(s, t);
//printf("%d %d\n",full_flow, flow);
if(full_flow == flow){
L = mid + 1;
ans = mid;
}
else R = mid;
}
printf("%d\n", ans);
}
int main()
{
freopen("in.txt","r",stdin);
int T; scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i = 0; i < n; ++i) scanf("%d",&C[i]);
for(int i = 0; i < n; ++i) scanf("%s",G2[i]);
//for(int i = 0; i < n; ++i) printf("%s\n",G2[i]);
solve();
}
return 0;
}