题目链接:Codeforces Round #304 (Div. 2) E. Soldier and Traveling
题意:
有n座城市,城市间有m条双向路。在一天时间内,城市里的人只能选择呆在城市里或到相邻的城市去(只能移动一次)。一开始城市i中有a[i]个人,经过一天的移动后,城市i中有b[i]个人,问是否有一种方案使该移动可行。
解题思路:
1、拆点:
因为题中描述的是城市人口移动前后的状态,所以可以把城市对时间拆点, 将城市X拆为X1和X2,X1代表移动前的城市,X2代表移动后的城市
2、建图:
源点S与X1连容量为a[x]的边,表示移动前城市x有a[x]人;X2与汇点T连容量为b[i]的边,表示移动后城市x有b[x]人。若城市X与城市Y之间有双向路,则X1与Y2连容量为∞的边,Y1与X2连容量为∞的边,表示这两个城市间可以迁移人口,且人口迁移不受限制;同时,X1与X2连容量为∞的边,表示有一部分人始终留在城市X。
3、输出:
若最大流结果 == ∑a[i],说明该移动可以实现,输出移动结果。遍历所有的 边,X1与Y2边 上容量的减少量即为从X到Y的迁 移人数,X1与X2边上 容量的减少量即为留在城市X的人数,用数组保存即可。
注意:
题目给出的∑a[i]可能 != ∑b[i],所以要对∑a[i] == ∑b[i]进行判断。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<utility>
#define JOJO cout<<"JOJO"<<endl;
#define TW system("pause");
using namespace std;
const long long INF = ~0ull>>2;
const int Inf = 0x3f3f3f3f;
const int maxn = 1e3+5;
int Map[105][105];
int head[maxn], cur[maxn];
int lev[maxn];
int t;
int S = 0, T;
struct tagedge{
int from, to, cap, next;
}edge[maxn*2];
void AddEdge(int u, int v, int w)
{
edge[t].from = u;
edge[t].to = v;
edge[t].cap = w;
edge[t].next = head[u];
head[u] = t++;
return;
}
bool LevelGraph()
{
queue<int> q;
fill(lev, lev+maxn, -1);
lev[S] = 0;
q.push(S);
while (!q.empty()){
int k = q.front();
q.pop();
for (int i = head[k]; i != -1; i = edge[i].next){
int v = edge[i].to;
if (lev[v] == -1 && edge[i].cap > 0){
lev[v] = lev[k] + 1;
q.push(v);
}
}
}
if (lev[T] == -1)
return false;
else
return true;
}
int FindPath(int k, int flow_in)
{
if (k == T)
return flow_in;
int flow_out = 0;
for (int i = cur[k]; i != -1; i = edge[i].next){
cur[k] = i;
int v = edge[i].to;
if (lev[v] == lev[k]+1 && edge[i].cap > 0){
int flow = FindPath(v, min(flow_in, edge[i].cap));
if (flow == 0) continue;
flow_in -= flow;
flow_out += flow;
edge[i].cap -= flow;
edge[i^1].cap += flow;
if (flow_in == 0) break;
}
}
return flow_out;
}
int Dinic()
{
int res = 0;
while (LevelGraph()){
memcpy(cur, head, sizeof(head));
res += FindPath(S, Inf);
}
return res;
}
void init()
{
fill(head, head+maxn, -1);
t = 0;
return;
}
int main()
{
init();
int n, m;
int sum_a = 0, sum_b = 0;
scanf("%d%d", &n, &m);
T = 2*n+5;
for (int i = 1; i <= n; i++){
int a;
scanf("%d", &a);
sum_a += a;
/*源点S与X1连容量为a[x]的边,
表示移动前城市x有a[x]人*/
AddEdge(S, i, a);
AddEdge(i, S, 0);
/*X1与X2连容量为∞的边,
表示有一部分人始终留在城市X*/
AddEdge(i, n+i, Inf);
AddEdge(n+i, i, 0);
}
for (int i = 1; i <= n; i++){
int b;
scanf("%d", &b);
sum_b += b;
/*X2与汇点T连容量为b[i]的边,
表示移动后城市x有b[x]人*/
AddEdge(n+i, T, b);
AddEdge(T, n+i, 0);
}
while (m--){
int u, v;
scanf("%d%d", &u, &v);
/*X1与Y2连容量为∞的边,
Y1与X2连容量为∞的边,
表示这两个城市间可以迁移人口,
且人口迁移不受限制*/
AddEdge(u, n+v, Inf);
AddEdge(n+v, u, 0);
AddEdge(v, n+u, Inf);
AddEdge(n+u, v, 0);
}
if (sum_a != sum_b)
cout << "NO";
else{
int res = Dinic();
if (res != sum_a){
cout << "NO";
}
else{
for (int k = 1; k <= n; k++){
for (int i = head[k]; i != -1; i = edge[i].next){
int v = edge[i].to;
if (v != S){ //有一条反向边连向源点,要排除
Map[k][v-n] = Inf - edge[i].cap;
}
}
}
cout << "YES" << endl;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
printf("%d ", Map[i][j]);
}
printf("\n");
}
}
}
return 0;
}
本文详细解析了Codeforces Round #304 (Div.2) E题“Soldier and Traveling”的解题思路,介绍了如何通过拆点和建图的方法解决城市间人口迁移问题,并提供了完整的代码实现。
&spm=1001.2101.3001.5002&articleId=105376747&d=1&t=3&u=6644c1c393a04713ad1739fb0763d008)
198

被折叠的 条评论
为什么被折叠?



