[POJ]1895 Bring Them There 网络流 动态加层

Bring Them There
Time Limit: 5000MS Memory Limit: 30000K
Total Submissions: 817 Accepted: 208 Special Judge

Description

By the year 3141, the human civilization has spread all over the galaxy. The special hypertunnels are used to travel from one star system to another. To use the hypertunnel, you fly to a special location near the source star using your spaceship, activate the hyperjumper, fly through the hypertunnel, get out near your destination star and fly to the planet you need. The whole process takes exactly one day. A small drawback of the system is that for each tunnel every day only one spaceship can travel using this tunnel. 

You are working in the transportation department of the "Intergalaxy Business Machines" company. This morning your boss has assigned a new task to you. To run the programming contest IBM needs to deliver K supercomputers from Earth where the company headquarters are located to the planet Eisiem. Since supercomputers are very large, one needs the whole spaceship to carry each supercomputer. You are asked to find a plan to deliver the supercomputers that takes as few days as possible. Since IBM is a very powerful corporation, you may assume that any time you need some tunnel for hyperjump, it is at your service. However, you still can use each tunnel only once a day. 

Input

The first line of input contains N --- the number of star systems in the galaxy, M --- the number of tunnels, K --- the number of supercomputers to be delivered, S --- the number of the solar system (the system where planet Earth is) and T --- the number of the star system where planet Eisiem is (2 <= N <= 50, 1 <= M <= 200, 1 <= K <= 50, 1 <= S, T <= N , S != T ). 

Next M lines contain two different integer numbers each and describe tunnels. For each tunnel the numbers of star systems that it connects are given. The tunnel can be traveled in both directions, but remember that each day only one ship can travel through it, in particular, two ships cannot simultaneously travel through the same tunnel in opposite directions. No tunnel connects a star to itself and any two stars are connected by at most one tunnel. 

Output

On the first line of output print L --- the fewest number of days needed to deliver K supercomputers from star system S to star system T using hypertunnels. Next L lines must describe the process. Each line must start with Ci --- the number of ships that travel from one system to another this day. Ci pairs of integer numbers must follow, pair A, B means that the ship number A travels from its current star system to star system B. It is guaranteed that there is a way to travel from star system S to star system T .

Sample Input

6 7 4 1 6
1 2
2 3
3 5
5 6
1 4
4 6
4 3

Sample Output

4
2 1 2 2 4
3 1 3 2 6 3 4
3 1 5 3 6 4 4
2 1 6 4 6

Source

[Submit]   [Go Back]   [Status]   [Discuss]

          一开始感觉这道题是大水题,结果发现每条路径只能经过一次,并且是一天只能经过一次,即第二天就可以又被经过了.完全没有头绪.想到用网络流,但是并不知道如何来建图.默默的点开了别人的题解....动态加层真的是很妙.那么每层的点是什么?两层之间又是如何连边?                               我们设花的最少天数为day,从小到大加,每day++即新的一天,我们就在原来的网络上加上一层,两层之间的线路表示一天.这就是动态加层.

     对于每一层,都是一个完整的图.每一层都是所有的点,但是我们连边不在本层之间互相连,而是连向下一层该连的点.比如说u,v之间有一条边,设下一层完整的图里v的编号是v+n,那么我们就将u向v+n连边,连流量为1的边,表示只允许一个飞船经过.注意我们两层之间就表示了一天的路程,所以我们在两层之间建容量为1的边,就恰好的表示了这一天某一条边只允许一个飞船经过,巧妙地符合了题意,所以对于本层的点,点与点之间互不相连.

        除此之外,我们还对某层的点i,向下一层的i(i+n)连一条容量为inf的边.这是什么意思呢?这其实表示的是某些飞船在i点无路可走(或者说某条边特别优只是今天暂时占用,但等待也是值得的),可以留在i点,所以到了下一天还是在i天,由于一个点可以待很多飞船(只有边有限制),所以容量为inf.           

       现在该谈谈源点和汇点的问题了.我们将源点S连向第一层的太阳系的点(题目中给出的),容量为k,表示这几天这k艘飞船的路程都从第0天(两层之间为1天,可以理解为两层之间为时间段,而每一层是每一天结束的时刻)的出发点开始的.而每一层的终点(题目中给出的)连向汇点T,容量为inf,表示每一天结束(上面所说到的时刻)又一些飞船已经到达了终点,因为每天结束时到达终点的飞船或多或少,所以容量为inf.最后这整个网络跑出的最大流若为k就成功,若仍不满足k,那么就再加一层(多放宽一天的时限),加一层跑一次直到为最大流k为止.

      路径输出枚举每个飞船就可以了,到一个地方若反向边有容量(表示这里在最大流中被走过,这是预设的最优方案),就此边容量加1,反向边-1(表示当前枚举的这个飞船今天走过了这条边,这是最优方案的实际实施),然后就去找这个边连向的v,表示这个飞船通过这条边经过一天的时间到达了v,再对v进行同样的操作,每次操作记录下路径.这一段可以结合代码理解(有注释).

     Tips:1.每次加层不代表重新建图,只需要在原来的网络上继续增广即可,时间复杂度实际上是没有那么多的.大概是最后层2.可以自己画图理解理解.

The End.

#include<stdio.h>
#include<queue>
#include<cstring>
using namespace std;
const int N=10010;
const int maxn=100010;
const int inf=1000000000;
int k,to,from,S,T,n,m,day,num=1,ans;
int f[maxn],t[maxn],dis[maxn],h[maxn];
inline const int read(){
    register int f=1,x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return f*x;
}
struct edge{
    int nxt,v,c;
}e[maxn*2];
void add(int u,int v,int c){
    e[++num].v=v,e[num].nxt=h[u],e[num].c=c,h[u]=num;
    e[++num].v=u,e[num].nxt=h[v],e[num].c=0,h[v]=num;
}
bool bfs(){
    queue<int> q;
    memset(dis,-1,sizeof(dis));
	q.push(S);
	dis[S]=0;
	while(!q.empty()){
	   int u=q.front();q.pop();
	   for(int i=h[u];i;i=e[i].nxt){
	       int v=e[i].v;
	       if(dis[v]==-1&&e[i].c){
	          dis[v]=dis[u]+1;
	          q.push(v);
		   }
	   }
	}
    return dis[T]!=-1;
}
int dfs(int u,int a){
    if(u==T) return a;
    int flow=0,f;
	for(int i=h[u];i;i=e[i].nxt){
	    int v=e[i].v;
	    if(dis[v]==dis[u]+1&&e[i].c){
	       f=dfs(v,min(e[i].c,a));
	       e[i].c-=f,e[i^1].c+=f;
		   a-=f,flow+=f;
		   if(a==0) return flow;		
		}
	}
	if(!flow) dis[u]=-1;
	return flow;
}
int days[N],id[N][60],go[N][60];
void find(int u,int who,int date){
    for(int i=h[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(e[i^1].c){
           e[i].c++;
           e[i^1].c--;
           if(v==T) return;
           if(v==u+n) find(v,who,date+1);
           else{
              days[date]++;
              id[date][days[date]]=who;
              go[date][days[date]]=(v-1)%n+1;
              find(v,who,date+1);
		   }
		   return;//只用找到一条合法路径(有special judge) 
		}
	}
}
int main(){
    n=read(),m=read(),k=read(),from=read(),to=read();
    for(int i=1;i<=m;i++) f[i]=read(),t[i]=read();
    day=0,ans=0;
    S=0,T=20000;
    add(S,from,k),add(to,T,inf);
    while(1){
	    while(bfs()) ans+=dfs(S,inf);
	    if(ans==k) {printf("%d\n",day);break;}
		for(int i=1;i<=m;i++) add(f[i]+day*n,t[i]+(day+1)*n,1),add(t[i]+day*n,f[i]+(day+1)*n,1); //这一步加边与下一步加边换个顺序就会WA. 
        for(int i=1;i<=n;i++) add(i+day*n,i+(day+1)*n,inf);//想不通为什么,顺序反了但最后建的图应该长得都一样啊,大神帮忙看一下 
	    add(to+(day+1)*n,T,inf);//对于上一行的问题,我用其他人的代码试了一下,搞反了顺序也都WA了 ...
	    day++;
	}
	for(int i=1;i<=k;i++) find(S,i,0);
	for(int i=1;i<=day;i++){
	    printf("%d ",days[i]);
	    for(int j=1;j<=days[i];j++)
	     printf("%d %d ",id[i][j],go[i][j]);
	    puts("");
	}
	return 0;
}




                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值