zoukankan      html  css  js  c++  java
  • Luogu 2469 [SDOI2010]星际竞速 / HYSBZ 1927 [Sdoi2010]星际竞速 (网络流,最小费用流)

    Luogu 2469 [SDOI2010]星际竞速 / HYSBZ 1927 [Sdoi2010]星际竞速 (网络流,最小费用流)

    Description

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

    Input

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

    Output

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

    Sample Input

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

    Sample Output

    12

    Http

    Luogu:https://www.luogu.org/problem/show?pid=2469#sub
    HYSBZ:https://vjudge.net/problem/HYSBZ-1927

    Source

    网络流,最小费用最大流

    题目大意

    有n个星球,m条从小编号到大编号的星球的单项航道,每条路径都有一个花费。另外可以通过跳跃的方法,从一个星球跳到星球u,其代价为C[u]。现在从不属于这n个星球的一个星球出发,求不重复遍历所有星球的最小代价。

    解决思路

    我们用最小费用最大流来解决这道问题。
    对于每一个星球我们把其拆成两个点,入点和出点。我们在每一个入点和源点之间连一条代价为0,流量为1的点,在每一个出点和汇点之间同样连一条代价为0,流量为1的点,这样是为了每个点只走一次且必须走一次。
    对于跳跃的情况,假设我们跳到u星球的代价是cost,那么我们在源点与u的出点连一条代价为cost,流量为1的边。
    对于每一条航道u->v,代价为cost,我们在u的入点和v的出点之间连一条代价为cost,流量为1的边。
    注意,在这张图中,我们连的所有边的流量都是1,这样是为了保证每一个星球只走一次。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int maxN=2010;
    const int maxM=40001;
    const int inf=2147483647;
    
    class Edge
    {
    public:
    	int u,v,cost,flow;//一条边的出发点,结束点,代价,残量
    };
    
    int n,m;
    int cnt=-1;
    int Head[maxN];
    int Next[maxM];
    Edge E[maxM];
    int dist[maxN];//spfa中0到i的代价
    int pre[maxN];//spfa中i点的来源边的编号
    int flow[maxN];//spfa中到达某点的流量
    bool inqueue[maxN];
    
    void Add_Edge(int u,int v,int cost,int flow);//添加边
    void _Add(int u,int v,int cost,int flow);
    bool spfa();//spfa求增广路
    
    int main()
    {
    	memset(Head,-1,sizeof(Head));
    	memset(Next,-1,sizeof(Next));
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)//我们定义源点为0,汇点为n*2+1
    	{
    		int cost;
    		scanf("%d",&cost);
    		Add_Edge(0,i,0,1);//添加源点到入点的边
    		Add_Edge(0,i+n,cost,1);//添加源点直接连到出点的边
    		Add_Edge(i+n,n*2+1,0,1);//添加出点到汇点的边
    	}
    	for (int i=1;i<=m;i++)
    	{
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		if (u>v)
    			swap(u,v);
    		Add_Edge(u,v+n,w,1);//添加入点到出点的边
    	}
    	int Ans=0;
    	while (spfa())//spfa增广
    	{
    		int now=n*2+1;
    		int last=pre[now];
    		while (now!=0)//修改残量
    		{
    			E[last].flow-=flow[n*2+1];
    			E[last^1].flow+=flow[n*2+1];
    			now=E[last].u;
    			last=pre[now];
    		}
    		Ans+=dist[n*2+1];//累计代价
    	}
    	cout<<Ans<<endl;
    	return 0;
    }
    
    void Add_Edge(int u,int v,int cost,int flow)
    {
    	_Add(u,v,cost,flow);//加正边
    	_Add(v,u,-cost,0);//加反边
    }
    
    void _Add(int u,int v,int cost,int flow)
    {
    	cnt++;
    	Next[cnt]=Head[u];
    	Head[u]=cnt;
    	E[cnt].u=u;
    	E[cnt].v=v;
    	E[cnt].cost=cost;
    	E[cnt].flow=flow;
    }
    
    bool spfa()//spfa求增广路
    {
    	memset(pre,-1,sizeof(pre));//初始化
    	memset(dist,127,sizeof(dist));
    	memset(flow,0,sizeof(flow));
    	memset(inqueue,0,sizeof(inqueue));
    	queue<int> Q;
    	while (!Q.empty())
    		Q.pop();
    	flow[0]=inf;//源点入队
    	dist[0]=0;
    	inqueue[0]=1;
    	Q.push(0);
    	do
    	{
    		int u=Q.front();
    		Q.pop();
    		inqueue[u]=0;
    		for (int i=Head[u];i!=-1;i=Next[i])
    		{
    			int v=E[i].v;
    			if ((E[i].flow>0)&&(dist[v]>dist[u]+E[i].cost))
    			{
    				dist[v]=dist[u]+E[i].cost;
    				pre[v]=i;
    				flow[v]=min(flow[u],E[i].flow);
    				if (inqueue[v]==0)
    				{
    					Q.push(v);
    					inqueue[v]=1;
    				}
    			}
    		}
    	}
    	while (!Q.empty());
    	if (flow[n*2+1]==0)//当汇点流量为0时,说明不存在增广路,增广结束
    		return 0;
    	return 1;
    }
    
  • 相关阅读:
    linux eclipse cdt make error 127
    centos 安装网络错误
    c++ string 结束符‘00’
    北京市工资交税情况
    gprof参数说明及常见错误
    【转】Eclipse Plugin 在 Console上打印出message
    C++做client Java做客户端传送数据
    word统计章节字数
    【转】Profiling application LLC cache misses under Linux using Perf Events
    【转】eclipse插件开发,调试运行,导出与安装
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7265214.html
Copyright © 2011-2022 走看看