poj也有这题,但数据很弱,建议去UVa上去交
链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=36127
题意:n个点m个双向边,每天每条边最大只能用一次(要么u->v运一个,要么v->u运一个),把k台电脑从S都运到T至少需要几天,并输出解。(n <= 50,m <= 200, k <= 50),保证有解。
思路:答案一定<=100,所以枚举天数,每次增加一天并尝试将增广使其总流量为k,直到增广满足条件为止,然后根据残余网络中的流量流向情况输出解。
如何建图:抓住每条边每天只能流一次,所以我们必须把这些边按天数拆,具体实现是拆点的。把每个点拆成u0,u1....ui,表示第i天电脑在u节点,对于原边<u,v> 连 所以 u(i)->v(i+1),然后u(i)->u(i+1)连边(表示原地不动)。
具体实现:每当当前的总流量< k时,就把天数加一,再在图中新加n个点(表示新的天数)并添加所需要的边。
可以将S的第0天的点看成源点(因为没有连向它的边)。 假如当前进行到了第t天,那么我把点T的第t天节点看成汇点,增广使其总流量为k(不到k就增广到最接近k)。 直到总流量为k时就停止。
输出解:模拟, 对于每一天分别输出解,pos[i]记录第i台电脑当前天在原图的节点编号, 对于这天,枚举新图第一天的所有边,有流量流过就让某个i走一下,并保存一下,然后在处理一下,具体看代码吧。
注意点:1. 在跑最网络流时,不是把流量增广到最大,而是尽可能的增广到k
2. 输出解的时候假如有U1--->V2 U2--->V1 两条边都有流量,不满足题意,但可以看成两台电脑都原地不动,自然这种情况这两条边就相互抵消了,不用记录下来。 (具体实现:建边的时候可以建在一起,然后方便一起枚举这2条边)
总结:1.建边的时候稍微把边排版了一下,方便了枚举,让输出解变得很容易。
2.在原有的图中选S,T
3.每次增加天数,把节点增加到数组的后面,然后在同一个图上进行增广,
而不是每次重新建图再增广
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 5010;
const int inf = 1e9;
int n, m, k, S, T;
struct Edges {
int v, c, next;
Edges(int v, int c, int next) :
v(v), c(c), next(next) {
}
Edges() {
}
} edge[10000007];
int head[maxn], E;
void add(int s, int t, int c) {
edge[E] = Edges(t, c, head[s]);
head[s] = E++;
edge[E] = Edges(s, 0, head[t]);
head[t] = E++;
}
void init() {
memset(head, -1, sizeof(head));
E = 0;
}
int gap[maxn], dis[maxn], pre[maxn], cur[maxn];
int sap(int s, int t, int n, int lim) {// s 源点,t汇点,n顶点总数, 限制最大增广的流量
int i;
for(i = 0; i <= n; i++) {
dis[i] = gap[i] = 0;
cur[i] = head[i];
}
gap[0] = n;
int u = pre[s] = s, maxf = 0, aug = lim, v;
while(dis[s] < n) {
loop: for(i = cur[u]; ~i; i = edge[i].next) {
v = edge[i].v;
if(edge[i].c && dis[u] == dis[v] + 1) {
aug = min(aug, edge[i].c);
pre[v] = u;
cur[u] = i;
u = v;
if(u == t) {
while(u != s) {
u = pre[u];
edge[cur[u]].c -= aug;
edge[cur[u] ^ 1].c += aug;
}
maxf += aug;
lim -= aug;
aug = lim;
if(!lim) return maxf;
}
goto loop;
}
}
int d = n;
for(i = head[u]; ~i; i = edge[i].next) {
v = edge[i].v;
if(edge[i].c && dis[v] < d) {
d = dis[v];
cur[u] = i;
}
}
if(!(--gap[dis[u]]))
break;
++gap[dis[u] = d + 1];
u = pre[u];
}
return maxf;
}
//*************************************
int u[203], v[203];
int ans;
void solve() {
int i, j, maxf = 0;
ans = 0;
init();
while(maxf < k) {
ans++;
for(i = 0; i < n; i++)
add((ans-1)*n+i, ans*n+i, inf);
for(i = 0; i < m; i++) {
add((ans-1)*n+u[i]-1, ans*n+v[i]-1, 1);
add((ans-1)*n+v[i]-1, ans*n+u[i]-1, 1);
}
maxf += sap(S-1, T+ans*n-1, (ans+1)*n, k-maxf);
}
printf("%d\n", ans);
}
int main() {
int i, j;
while( ~scanf("%d%d%d%d%d", &n, &m, &k, &S, &T)) {
for(i = 0; i < m; i++) scanf("%d%d", &u[i], &v[i]);
solve();
vector <int> pos(k, S);
int idx = 0;
for(int d = 1; d <= ans; d++) {
idx += (n<<1);
vector <int> a;
vector <int> b;
vector <bool> vis(k, 0);
for(i = 0; i < m; i++) {
//同时枚举2条边避免上面第2个注意点
int f1 = edge[idx^1].c; idx += 2;
int f2 = edge[idx^1].c; idx += 2;
if(f1 && !f2) a.push_back(u[i]), b.push_back(v[i]);
if(!f1 && f2) a.push_back(v[i]), b.push_back(u[i]);
}
printf("%d", a.size());
for(i = 0; i < a.size(); i++)
for(j = 0; j < k; j++)
if(!vis[j] && a[i] == pos[j]) {
printf(" %d %d", j+1, b[i]);
pos[j] = b[i];
vis[j] = 1;
break;
}
puts("");
}
}
return 0;
}