Random Maze
Time Limit: 10000/3000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 1347 Accepted Submission(s): 501
1.There is only one entrance and one exit.
2.All the road in the maze are unidirectional.
3.For the entrance, its out-degree = its in-degree + 1.
4.For the exit, its in-degree = its out-degree + 1.
5.For other node except entrance and exit, its out-degree = its in-degree.
There is an directed graph, your task is removing some edge so that it becomes a random maze. For every edge in the graph, there are two values a and b, if you remove the edge, you should cost b, otherwise cost a.
Now, give you the information of the graph, your task if tell me the minimum cost should pay to make it becomes a random maze.
The rest of the test file contains T blocks.
For each test case, there is a line with four integers, n, m, s and t, means that there are n nodes and m edges, s is the entrance's index, and t is the exit's index. Then m lines follow, each line consists of four integers, u, v, a and b, means that there is an edge from u to v.
2<=n<=100, 1<=m<=2000, 1<=s, t<=n, s != t. 1<=u, v<=n. 1<=a, b<=100000
2 2 1 1 2 2 1 2 3 5 6 1 4 1 2 3 1 2 5 4 5 5 3 2 3 3 2 6 7 2 4 7 6 3 4 10 5
Case 1: impossible Case 2: 27
题意:选择某些边建造或不建造,使得起点的出度比入度大1,终点的入度比出度大1,其它点入度等于出度,求这个最小费用。
思路:如果人为添加t->s的边,那么图中所有顶点要满足的条件都是一样的了,我们以此为目的来建图。
对于每条边,我们只有两种操作,要么保留要么删除,那么先假设两种操作都能满足条件,我们就可以选择花费小的操作来执行,最后再根据实际情况调整。
首先不加入任何边,在添加或删除(不加入)边的过程中,对每个顶点v记录in[v]为其当前入度,out[v]为其出度,sum为当前的总花费。
那么对于每条边,如果a<=b,那么保留这条边,in[v]++,out[u]++,sum+=a,然后连边v->u,流量1,费用为b-a(如果删除这条边的费用)
如果b<a,那么删去这条边,sum+=b,然后连边u->v,流量1,费用为a-b(如果保留这条边的费用)。
然后我们人为的加入一条t->s,直接in[s]++,out[t]++,使得图中所有点处于相同的状况。
设立超级源汇S、T,对于原图的每个点i,如果in[i]>out[i],则连边S->i,流量为in[i]-out[i], 费用为0,否则连边i->T,流量为out[i]-in[i],费用为0。至此,建图完成。
现在求S到T的费用流mincost,然后检查从S发出的边,如果全部满流则有解,答案就是sum+mincost,否则无解。
这样建图的意义:例如对点i,in[i]>out[i],说明当前该点入度大于出度,那么我们把之前删除的以i为起点的边添加回来 或者把之前保留的以i为终点的边删除,现在边的费用其实是改变边状态所需要额外付的费用,而最小费用流所求的就是全部调整的总费用了,于是答案就是sum(初始操作的费用)+mincost(额外付出的费用)。
AC代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct node
{
int u,v,cost,cap,next;
}edge[10010];
int T,t,n,m,Head[110],tot,C,F,F2,dis[110],INF=1e9,pre[110],f[110];
int in[110],out[110];
bool vis[110];
queue<int> qu;
void add(int u,int v,int cost,int cap)
{
edge[tot].u=u;
edge[tot].v=v;
edge[tot].cost=cost;
edge[tot].cap=cap;
edge[tot].next=Head[u];
Head[u]=tot++;
}
int _spfa()
{
int i,j,k,u,v,p;
for(i=0;i<=n;i++)
dis[i]=INF;
memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));
memset(f,0,sizeof(f));
dis[0]=0;vis[0]=1;
qu.push(0);
f[0]=INF;
pre[0]=-1;
while(!qu.empty())
{
u=qu.front();
qu.pop();
vis[u]=0;
for(i=Head[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(edge[i].cap && dis[u]+edge[i].cost<dis[v])
{
dis[v]=dis[u]+edge[i].cost;
f[v]=min(f[u],edge[i].cap);
pre[v]=i;
if(!vis[v])
{
qu.push(v);
vis[v]=1;
}
}
}
}
if(dis[n]==INF)
return 0;
C+=f[n]*dis[n];
F+=f[n];
if(F==F2)
return 0;
for(i=pre[n];i!=-1;i=pre[edge[i^1].v])
{
edge[i].cap-=f[n];
edge[i^1].cap+=f[n];
}
return 1;
}
int main()
{
int i,j,k,u,v,a,b;
scanf("%d",&T);
for(t=1;t<=T;t++)
{
scanf("%d%d%d%d",&n,&m,&a,&b);
n++;
memset(Head,-1,sizeof(Head));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
tot=0;C=0;F=0;F2=0;
in[a]++;out[b]++;
for(i=1;i<=m;i++)
{
scanf("%d%d%d%d",&u,&v,&a,&b);
if(a<=b)
{
add(v,u,b-a,1);
add(u,v,a-b,0);
out[u]++;in[v]++;
C+=a;
}
else
{
add(u,v,a-b,1);
add(v,u,b-a,0);
C+=b;
}
}
for(i=1;i<n;i++)
if(out[i]>in[i])
{
add(i,n,0,out[i]-in[i]);
add(n,i,0,0);
}
else if(in[i]>out[i])
{
add(0,i,0,in[i]-out[i]);
add(i,0,0,0);
F2+=in[i]-out[i];
}
while(_spfa())
{
}
if(F!=F2)
printf("Case %d: impossible\n",t);
else
printf("Case %d: %d\n",t,C);
}
}