• 【bzoj1570】[JSOI2008]Blue Mary的旅行 动态加边网络流


    题目描述

    在一段时间之后,网络公司终于有了一定的知名度,也开始收到一些订单,其中最大的一宗来自B市。Blue Mary决定亲自去签下这份订单。为了节省旅行经费,他的某个金融顾问建议只购买U航空公司的机票。U航空公司的所有航班每天都只有一班,并且都是上午出发当天下午到达的,所以他们每人每天只能坐一班飞机。经过调查,他们得到了U航空公司经营的所有航班的详细信息,这包括每一航班的出发地,目的地以及最多能买到的某一天出发的票数。(注意: 对于一个确定的航班,无论是哪一天,他们最多能买到的那一天出发的票数都是相同的。) Blue Mary注意到他们一定可以只乘坐U航空公司的航班就从A市到达B市,但是,由于每一航班能买到的票的数量的限制,他们所有人可能不能在同一天到达B市。所以现在Blue Mary需要你的帮助,设计一个旅行方案使得最后到达B市的人的到达时间最早。

    输入

    第一行包含3个正整数N,M和T。题目中会出现的所有城市分别编号为1,2,…,N,其中城市A编号一定为1,城市B编号一定为N. U公司一共有M条(单向)航班。而连Blue Mary在内,公司一共有T个人要从A市前往B市。 以下M行,每行包含3个正整数X,Y,Z, 表示U公司的每一条航班的出发地,目的地以及Blue Mary最多能够买到的这一航班某一天出发的票数。(即:无论是哪一天,Blue Mary最多只能买到Z张U航空公司的从城市X出发到城市Y的机票。) 输入保证从一个城市到另一个城市的单向航班最多只有一个。

    输出

    仅有一行,包含一个正整数,表示最后到达B市的人的最早到达时间。假设他们第一次乘飞机的那一天是第一天。

    样例输入

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

    样例输出

    6


    题解

    动态加边网络流

    由于题目中限定一人每天只能坐一次飞机,所以可以将飞机的运行时间看作一天,出发时间看作第0天。

    这样就可以拆点,将每个除1和n以外的点拆成totaltime+1个,看作0~totaltime+1层。注意点1和点n不需要拆,即1和n无论在多少层,编号始终为1和n。

    首先加S->1,容量为T,代表T个人。

    然后枚举时间t,设点x在第k层所拆的点为(x,k),则加边(i,t-1)->(i,t),容量为inf;对于原图中的边x->y(z),加边(x,t-1)->(y,t),容量为z。

    这样动态加边后把n看做汇点,跑dinic,判断一下是否满流即可。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define N 10010
    #define M 1000010
    #define inf 0x3f3f3f3f
    #define pos(i , j) (i == 1 || i == n) ? i : (n - 2) * (j) + i + 1
    using namespace std;
    queue<int> q;
    int head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N] , dx[M] , dy[M] , dz[M];
    void add(int x , int y , int z)
    {
    	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    	to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
    }
    bool bfs()
    {
    	int x , i;
    	memset(dis , 0 , sizeof(dis));
    	while(!q.empty()) q.pop();
    	dis[s] = 1 , q.push(s);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop();
    		for(i = head[x] ; i ; i = next[i])
    		{
    			if(val[i] && !dis[to[i]])
    			{
    				dis[to[i]] = dis[x] + 1;
    				if(to[i] == t) return 1;
    				q.push(to[i]);
    			}
    		}
    	}
    	return 0;
    }
    int dinic(int x , int low)
    {
    	if(x == t) return low;
    	int temp = low , i , k;
    	for(i = head[x] ; i ; i = next[i])
    	{
    		if(val[i] && dis[to[i]] == dis[x] + 1)
    		{
    			k = dinic(to[i] , min(temp , val[i]));
    			if(!k) dis[to[i]] = 0;
    			val[i] -= k , val[i ^ 1] += k;
    			if(!(temp -= k)) break;
    		}
    	}
    	return low - temp;
    }
    int main()
    {
    	int n , m , k , i , j , ans = 0;
    	scanf("%d%d%d" , &n , &m , &k);
    	s = 0 , t = n , add(s , 1 , k);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &dx[i] , &dy[i] , &dz[i]);
    	for(i = 1 ; i <= n + k ; i ++ )
    	{
    		for(j = 2 ; j < n ; j ++ ) add(pos(j , i - 1) , pos(j , i) , inf);
    		for(j = 1 ; j <= m ; j ++ ) add(pos(dx[j] , i - 1) , pos(dy[j] , i) , dz[j]);
    		while(bfs()) ans += dinic(s , inf);
    		if(ans == k)
    		{
    			printf("%d
    " , i);
    			return 0;
    		}
    	}
    	return 0;
    }

     

  • 相关阅读:
    排列组合
    分治——最大连续数组和
    分治——最近点对
    Java数据类型
    4源代码的下载和编译
    3Git使用入门
    2.3搭建Android应用程序开发环境
    2.2安装JDK
    2.1Android底层开发需要哪些工具
    1.8小结
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6858277.html
Copyright © 2020-2023  润新知