zoukankan      html  css  js  c++  java
  • Codeforces Round #712 (Div. 1) C. Travelling Salesman Problem

    题目链接:

    http://codeforces.com/contest/1503/problem/C

    Solution:

    考虑你的路径本质上是一个由有向边组成的环,求环上的边权和,所以起点并不重要

    每个环的边权(max(c_i,a_j-a_i)),注意到(c_i)不依赖于其他点,考虑提出来,边权可以化成(c_i+max(c_i,a_j-a_i-c_i))

    那么答案就是(sum c_i+sum max(0,a_j-a_i-c_i))

    前半部分直接算就是了,考虑后半部分,也就是如何安排环上点的顺序

    显然任意一种方案的路径都可以拆分为以某一点为起始,到(a_i)最大的点,再回到起点。这样拆分有什么好处呢?

    注意到从(a_i)值大的点走到小的,不会产生花费

    也就是说走到最大点后,按(a_i)降序走一遍未经过的点回起点,不会产生任何花费

    这样我们就只需要考虑前半段,也就是如何走到(a_i)最大的点就好了

    (a_j>a_i+c_i)时也不会产生花费

    所以我们以(a_i)为关键字升序排序,去求从首走到尾的最小花费。

    考虑走到某一个点(j),如果前面存在一个点(i),满足(a_j<a_i+c_i),那我可以先到(i)再到(j),也就是说我可以考虑不花费任何代价走到(j),否则需要额外花费代价去扩展,代价为(a_j-max(a_k+c_k))((k)表示所有在(j)前面的点)。

    过程可以设计为:从最小点出发,走到最大点,当在某一点(i)时,直接走到(i)后面(a)最大且值小于(max(a_k+c_k))的点(q)((k)表示所有在(i)前面的点),其中(i)(q)上的所有点我都可以免费的扩展到。然后再从(q)出发重复这个过程,如果没有符合条件的(q),那么就需要花费代价扩展到(q)的下一个点

    我们排序后直接从小到大(O(n))扫一遍,过程中维护下已经扩展了的点中的(max(a_i+c_i)),即可

    #include<bits/stdc++.h>
    using namespace std;
    struct city{
    	long long a,c;
    }ct[100005];
    int n;
    long long ans=0;
    bool cmp(city x,city y)
    {
    	if(x.a<y.a)
    	   return true;
    	return false;   
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%lld%lld",&ct[i].a,&ct[i].c);	
    	sort(ct+1,ct+1+n,cmp);
    	long long mx=0;
    	for(int i=1;i<=n-1;i++)
    	{
    		mx=mx>ct[i].a+ct[i].c?mx:ct[i].a+ct[i].c;
    		ans+=ct[i].c;
    		if(ct[i+1].a>mx)
    			ans+=ct[i+1].a-mx;	
    	}
    	ans+=ct[n].c;
    	printf("%lld",ans);	
    	return 0;
    } 
    
    小鳥の翼がついに大きくなって , 旅立ちの日だよ , 遠くへと広がる海の色暖かく , 夢の中で描いた絵のようなんだ , 切なくて時をまきもどしてみるかい ? No no no いまが最高! だってだって、いまが最高!
  • 相关阅读:
    IIS-Service Unavailable
    复制datatable,把类型变为字符串
    泛类型的使用
    线程间操作无效: 从不是创建控件“button1”的线程访问它。
    .dialog打开时执行方法
    更新系统时间
    复制对象
    如何安装windows服务
    ObjectARX创建文字
    设置cad进度条的arx代码
  • 原文地址:https://www.cnblogs.com/nanjolno/p/14697957.html
Copyright © 2011-2022 走看看