Uva12264Risk游戏——最大流+二分

题意:

给n个点的无权无向图(n<=100),每个点有一个非负数ai。若ai==0则此点归敌方所有,若ai>0则此点归你且上面有ai个属于你的士兵。保证至少有一个属于你的点与敌方的点相邻。你可以让你的每个士兵最多移动一次,每次可以待在原地或者去到相邻的属于你的领地,但每个点至少要留1各士兵,使得最薄弱的关口尽量坚固。关口是指与敌方点相邻的点,薄弱与坚固分别指兵少与兵多。

思路:

讲的很好:https://blog.csdn.net/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;
}


猜你喜欢

转载自blog.csdn.net/CY05627/article/details/92793104