cf1504. Travelling Salesman Problem
题意:
n个城市,编号1~n,每个城市有美丽值a[i],现在要从城市1出发,其他所有城市走一遍,最后回到城市1,城市i到j的花费为max(ci,aj-ai),ci为第i个城市的最低消费
最低的总消费是多少?
题解:
(题解来自官方题解)
费用 = max (ci ,aj - ai)= ci +max(0,aj-ai-ci)
因为所有城市都要经过,所以ci是固定消费,我们现在要做的就是最小化max(0,aj-ai-ci)的总和
我们注意ai,ci一定是大于0的,要让这个max最小化,我们就尽可能让他等于0,也就是aj - (ai+ci)<=0
如果ai>aj,那么从城市i到城市j就是免费的(这里不考虑固定的ci消费,只考虑max的情况),所以我们只需要找一条从a1到an的路,剩下的旅程可以免费完成,因为最短路径是最优的
贴上原题解(说实话我现在还不是很清楚,等脑子清楚了再解决)
2021/4/21
下午上java时突然明白了,为什么代码里取max(mx, ve[i].first + ve[i].second);
我们上面已经推导出每个c都是计算在内的,a是从小到大排序的,所以a[j]-a[i]一定大于等于0,c一定大于0,a[j]-a[i]-c[i]大小就不一定了,考虑到如果存在一个很大的ci,1可以通过它间接到达n,1借助当前的ci能到达的最高的位置x,及比当前的ai+aj大的ax+ay,这样比1直接到达n的费用更小。
代码:
#include <bits/stdc++.h>using namespace std;int main() {ios::sync_with_stdio(false);cin.tie(0);int n;cin >> n;vector<pair<long long, long long>> ve;long long ans = 0;for(int i = 0; i < n; i++) {long long a, c;cin >> a >> c;ve.push_back({a, c});ans += c;}sort(ve.begin(), ve.end());//以a为第一元素,b为第二元素//城市的美丽值是递增的 //for(int i=0;i<n;i++)// cout<<ve[i].first<<" "<<ve[i].second<<endl;long long mx = ve[0].first + ve[0].second;for(int i = 1; i < n; i++) {ans += max(0LL, ve[i].first - mx);mx = max(mx, ve[i].first + ve[i].second);}cout << ans << '\n';
}
#include <bits/stdc++.h>using namespace std;int main() {int n;cin >> n;vector<pair<long long, long long> > city;long long ans = 0;for(int i = 0; i < n; i++) {long long a, c;cin >> a >> c;city.push_back({a, c});ans += c;}sort(city.begin(), city.end());priority_queue<pair<long long, int>> Q;vector<bool> vis(n, false);Q.push({0, 0});while(!Q.empty()) {long long d; int i;tie(d, i) = Q.top();//返回指向绑定的输出流的指针。Q.pop();if(vis[i]) continue;vis[i] = true;if(i == n - 1) {ans -= d;break;}if(i > 0) {Q.push({d, i - 1});}int j = lower_bound(city.begin(), city.end(), make_pair(city[i].first + city[i].second, LLONG_MAX)) - city.begin() - 1;//LLONG_MAX均存在与头文件limits.h,表示long long int Q.push({d, j});if(j + 1 < n) {Q.push({d - city[j + 1].first + city[i].first + city[i].second, j + 1});}}cout << ans << '\n';
}