zoukankan      html  css  js  c++  java
  • 【bzoj1927】[Sdoi2010]星际竞速 有上下界费用流

    原文地址:http://www.cnblogs.com/GXZlegend/p/6832464.html


    题目描述

    10年一度的银河系赛车大赛又要开始了。作为全银河最盛大的活动之一,夺得这个项目的冠军无疑是很多人的梦想,来自杰森座α星的悠悠也是其中之一。赛车大赛的赛场由N颗行星和M条双向星际航路构成,其中每颗行星都有一个不同的引力值。大赛要求车手们从一颗与这N颗行星之间没有任何航路的天体出发,访问这N颗行星每颗恰好一次,首先完成这一目标的人获得胜利。由于赛制非常开放,很多人驾驶着千奇百怪的自制赛车来参赛。这次悠悠驾驶的赛车名为超能电驴,这是一部凝聚了全银河最尖端科技结晶的梦幻赛车。作为最高科技的产物,超能电驴有两种移动模式:高速航行模式和能力爆发模式。在高速航行模式下,超能电驴会展开反物质引擎,以数倍于光速的速度沿星际航路高速航行。在能力爆发模式下,超能电驴脱离时空的束缚,使用超能力进行空间跳跃——在经过一段时间的定位之后,它能瞬间移动到任意一个行星。天不遂人愿,在比赛的前一天,超能电驴在一场离子风暴中不幸受损,机能出现了一些障碍:在使用高速航行模式的时候,只能由每个星球飞往引力比它大的星球,否则赛车就会发生爆炸。尽管心爱的赛车出了问题,但是悠悠仍然坚信自己可以取得胜利。他找到了全银河最聪明的贤者——你,请你为他安排一条比赛的方案,使得他能够用最少的时间完成比赛。

    输入

    第一行是两个正整数N,M。第二行N个数A1~AN,其中Ai表示使用能力爆发模式到达行星i所需的定位时间。接下来M行,每行3个正整数ui,vi,wi,表示在编号为ui和vi的行星之间存在一条需要航行wi时间的星际航路。输入数据已经按引力值排序,也就是编号小的行星引力值一定小,且不会有两颗行星引力值相同。

    输出

    仅包含一个正整数,表示完成比赛所需的最少时间。

    样例输入

    3 3
    1 100 100
    2 1 10
    1 3 1
    2 3 1

    样例输出

    12


    题解

    有上下界费用流

    题目要求每个点只经过一次,所以把每个点拆成两个,之间连一条上下界均为1的边。

    对于瞬移,可以看作先流到某个特定的点,再流至点1~n。

    先建无源汇图,然后转化为费用流来求。

    对于这道题的具体建图方法:

    0->i(inf,ai),i+n->0(inf,0),s->i+n(1,0),i->t(i,0),u+n->v(inf,w)(u<v),然后跑最小费用最大流即可。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    queue<int> q;
    int head[2000] , to[100000] , val[100000] , cost[100000] , next[100000] , cnt = 1 , s , t , dis[2000] , from[2000] , pre[2000];
    void add(int x , int y , int v , int c)
    {
    	to[++cnt] = y , val[cnt] = v , cost[cnt] = c , next[cnt] = head[x] , head[x] = cnt;
    	to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , next[cnt] = head[y] , head[y] = cnt;
    }
    bool spfa()
    {
    	int x , i;
    	memset(dis , 0x3f , sizeof(dis));
    	memset(from , -1 , sizeof(from));
    	dis[s] = 0 , 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[x] + cost[i])
    				dis[to[i]] = dis[x] + cost[i] , from[to[i]] = x , pre[to[i]] = i , q.push(to[i]);
    	}
    	return ~from[t];
    }
    int mincost()
    {
    	int ans = 0 , i , k;
    	while(spfa())
    	{
    		k = inf;
    		for(i = t ; i != s ; i = from[i]) k = min(k , val[pre[i]]);
    		ans += k * dis[t];
    		for(i = t ; i != s ; i = from[i]) val[pre[i]] -= k , val[pre[i] ^ 1] += k;
    	}
    	return ans;
    }
    int main()
    {
    	int n , m , i , x , y , z;
    	scanf("%d%d" , &n , &m);
    	s = 2 * n + 1 , t = 2 * n + 2;
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &z) , add(0 , i , inf , z) , add(i + n , 0 , inf , 0) , add(s , i + n , 1 , 0) , add(i , t , 1 , 0);
    	while(m -- )
    	{
    		scanf("%d%d%d" , &x , &y , &z);
    		if(x > y) swap(x , y);
    		add(x + n , y , inf , z);
    	}
    	printf("%d
    " , mincost());
    	return 0;
    }

     

  • 相关阅读:
    二叉树的遍历
    深度优先遍历和广度优先遍历
    N的阶乘末尾有多少个0
    框架产生的历史
    Ansible--初始ansible
    日积跬步05
    日积跬步04
    日积跬步03
    日积跬步02
    日积跬步01
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6832464.html
Copyright © 2011-2022 走看看