原文地址:http://www.starvae.com/?p=288
题意:在游戏“倩女幽魂”中,有很多随机迷宫,它们都有如下特性:1.迷宫只有一个入口和一个出口;2.所有的路都是无向的;3.对于入口,他的出度等于入度+1;4.对于出口,他的入度等于出度+1;5.其他所有点的出度等于入度。现在给你一个有向图,你的任务是移走一些边使得它变成一个随机迷宫。每条边都有两个权值a和b,如果移走这条边,你会花费b,不然你会花费a。求使地图变成随机迷宫的最小花费。
难度:5
题解:题解: 用SPFA 或者 最小费用最大流解决。(1).题目的意思是在一个有向图中选择一些边使得每个点的入度出度相同, 出了起点和终点, 我们在起点和终点中加上一条终点指向起点的边就可以了。 我们给每条边定义新的权值, 大小为a-b, 这样我们的任务就转变成在新的图中寻找所有的负环, 然后将它转向为正环, 直到找到所有的负环。 还有一个要求是寻找到的负环中的其中一个必须包含新加上的终点指向起点的边。 这样就能保证起点和终点的度的特殊性。
如果没有包含这条边的负环, 那么就是impossible。用spfa就能解决, 但是速度慢,并且因为有重边的缘故, 判断负环的时候要特别注意一点。 我写了个用了4406 MS,时限给了3S, 如果有人能用SPFA过, 那着实仰慕。
(2).以上是最直接的方法.
下面是直接建图的方法。
我们的答案保存在sum中。
初始时每个点的in[] out[] 都为0, in[i] 表示第i这个点需要in[i]条指向i的边才能满足i这个点的入度平衡。
对于每条边,如果a <= b 那么建 v->u的边,流量为1, 权值为b-a。 sum+= a;
此时, u->v 被翻转, 所以in[v] ++, out[u] ++.
否则 建 u->v的边, 流量为1, 权值为a-b。 sum += b;
此时, u->v 没有被翻转, 所以in[] out[] 不改变
然后添加一条终点指向起点的边, 直接in[s] ++;out[t] ++;
然后新建超级源汇S,T。
对于每个点i, 如果in[i] > out[i] . 建边S->i, 权值为0, 流量为in[i] – out[i];
否则的话 建边 i->T ,权值为0, 流量为out[i] – in[i];
然后跑一次最小费用最大流。
如果最后从S出去的边没有满流的话 就是impossible。
否则答案即为sum+ mincost.
spfa代码:
#include<iostream>
#include<stdio .h>
#include<string .h>
#include <queue>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
/*
Author : Starvae
Time : 2011.9.26
Algorithm : Spfa
*/
int pre[1100];
struct EGDE
{
int u;
int v;
int dis;
int c;
int next;
}E[1000000];
int nv;
int ne;
int head[310];
int ans ;
void init(int n)
{
nv = n;
ne = 0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int dis,int c)
{
//printf(" %d %d %d %d\n",u,v,dis,c);
E[ne].c = c; E[ne].dis = dis; E[ne].u = u;E[ne].v = v;
E[ne].next = head[u]; head[u] = ne++;
E[ne].c = 0; E[ne].dis = -dis; E[ne].u = v;E[ne].v = u;
E[ne].next = head[v]; head[v] = ne++;
}
int du[310];
void work(int u)
{
int i,j,k;
int hash[1100]={0};
k = u;
while(1)
{
if(hash[u] == 1)
break;
hash[u] = 1;
u = E[pre[u]].u;
}
k = u;
// ans = 0;
while(1)
{
i = pre[u];
if(E[i].u == k)
break;
ans += E[i].dis;
E[i].c = 0;
E[i^1].c = 1;
u = E[i].u;
}
ans += E[i].dis;
E[i].c = 0;
E[i^1].c = 1;
return ;
}
void OUT()
{
for(int i = 0 ; i < ne; i ++)
{
if(E[i].c != 0)
{
printf(" %d %d %d %d\n",E[i].u, E[i].v, E[i].dis, E[i].c);
}
}
}
bool spfa()
{
//OUT();
long long dis[1100];
int i,j,k;
int val[310];
memset(du,0,sizeof(du));
int hash[1100];
queue<int>Q;
for(int i = 0 ; i < = nv; i ++)
val[i] = -100000000;
for(i=1;i<=nv;i++)
{
dis[i] = 0;
Q.push(i);
hash[i] = 1;
}
int flag = false;
while(!Q.empty())
{
int u = Q.front();
Q.pop();
hash[u] = 0;
for(i=head[u];i!=-1;i=E[i].next)
{
if(E[i].c == 0) continue;
int v = E[i].v;
if(dis[v] > dis[u] + E[i].dis)
{
dis[v] = dis[u] + E[i].dis;
pre[v] = i;
if(hash[v] == 0)
{
hash[v] = 1;
Q.push(v);
}
if(val[u] != dis[u])
{
val[u] = dis[u];
du[v] ++;
if(du[v] == nv)
{
work(v);
memset(du,0,sizeof(du));
flag = true;
}
}
}
}
}
return flag;
}
bool readint(int &ret){
int sgn;
char c;
c=getchar();
if(c==EOF)return true;
while(c!='-'&&c< '0'||c>'9')c=getchar();
sgn=(c=='-')?-1:1;
ret=(c=='-')?0:(c-'0');
while((c=getchar())>='0'&&c< ='9')ret=ret*10+(c-'0');
ret*=sgn;
return false;
}
int main()
{
int casT;
int cas = 1;
int n,m;
int s,t;
int u,v,a,b;
// freopen("in.in","r",stdin);
// freopen("spfa.out","w",stdout);
readint(casT);
// scanf("%d",&casT);
while(casT --)
{
// scanf("%d%d%d%d",&n,&m,&s,&t);
readint(n); readint(m); readint(s); readint(t);
init(n);
ans = 0;
for(int i = 0 ; i < m ; i ++)
{
// scanf("%d %d %d %d",&u,&v,&a,&b);
readint(u); readint(v); readint(a); readint(b);
ans += b;
if(u == v)
{
if(a - b <= 0)
{
ans += a-b;
}
continue;
}
addedge(u,v,a-b,1);
}
ans += 100000000;
addedge(t,s,-100000000,1);
while(spfa())
;
printf("Case %d: ",cas++);
if(ans >= 100000000)
printf("impossible\n");
else
printf("%d\n",ans);
}
return 0;
}
</algorithm></string></vector></queue></string></stdio></iostream>
MCMF代码:
#include<iostream>
#include<stdio .h>
#include<string .h>
#include <queue>
#include<vector>
#include<string>
using namespace std;
#define maxm 1000000
#define maxn 3000
#define INF 0x3fffffff
struct MCMF
{
struct EDGE
{
int u,v,next,f,w;
}E[maxm];
int maxflow;
int mincost;
int p[maxn];
int head[maxn];
int num;
int s,t;
int NV;
void add(int s,int t,int w,int c)
{
E[num].f = c;
E[num].next = head[s];
head[s] = num;
E[num].u = s;
E[num].v = t;
E[num++].w = w;
return;
}
void addedge(int s,int t,int w,int c)
{
//s->t cost = w, flow = c
add(s,t,w,c);
add(t,s,-w,0);
return ;
}
int Q[maxn*10],d[maxn];
bool spfa()
{
int i,j;
int le = 0, ri = 0;
bool ok[maxn] = {0};
for(i=0;i<nv ;i++)
d[i] = INF;
d[s] = 0;
ok[s] = 1;p[s] = -1;
Q[ri++] =s;
while(le != ri)
{
int u =Q[le++];
ok[u] = 0;
for(i=head[u];i!=-1;i=E[i].next)
{
int v = E[i].v;
if(E[i].f>0 && d[u]+E[i].w < d[v])
{
d[v] = d[u] + E[i].w;
p[v] = i;
if(!ok[v])
{
Q[ri++] = v;
ok[v] = 1;
}
}
}
}
if(d[t] < INF)
return 1;
return 0;
}
void slove()
{
int i;
int minflow = INF;
for(i=p[t];i!=-1;i=p[E[i].u])
{
if(minflow >E[i].f)
minflow = E[i].f;
}
for(i=p[t];i!=-1;i=p[E[i].u])
{
E[i].f -= minflow;
E[i^1].f += minflow;
mincost += E[i].w*minflow;
}
maxflow += minflow;
return ;
}
int MinCost_MaxFlow(int _s,int _t)
{
s = _s;
t = _t;
maxflow = 0;
mincost = 0;
while(spfa())
slove();
return mincost;
}
void init(int n)
{
NV= n;
memset(head,-1,sizeof(head));
num = 0;
}
}G;
int main()
{
int casT;
int cas;
int S,T;
int in[310], out[310];
int sum,suma,sumb;
int n,m;
int ss,tt;
scanf("%d",&casT);
cas = 1;
while(casT --)
{
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
scanf("%d%d%d%d",&n,&m,&ss,&tt);
S = 0;
T = n+1;
suma = sumb = sum = 0;
G.init(T+1);
while(m--)
{
int u,v,a,b;
scanf("%d%d%d%d",&u,&v,&a,&b);
suma += a;
sumb += b;
if(a < = b){
sum += a;
G.addedge(v,u,b-a,1);
in[v] ++; out[u] ++;
}
else{
sum += b;
G.addedge(u,v,a-b,1);
}
}
//添加一条tt->ss的边
in[ss] ++; out[tt] ++;
int needflow = 0;
for(int i = 1; i < = n; i ++)
if(in[i] > out[i]){
G.addedge(S, i, 0, in[i] - out[i]);
needflow += in[i] - out[i];
}else if(in[i] < out[i])
G.addedge(i, T, 0, out[i] - in[i]);
sum = sum + G.MinCost_MaxFlow(S, T);
if(G.maxflow < needflow)
printf("Case %d: impossible\n", cas++);
else{
printf("Case %d: %d\n", cas++, sum);
}
}
return 0;
}