题目链接:http://codeforces.com/contest/1130/problem/D2
贪心的策略还算比较容易找的,但是会比较难写。
贪心策略是每次都取离当前车站最远的一颗糖,这样能多经过几个车站多取几颗糖,省时间。
但是写起来特别麻烦。
但是如果你能把这道题的问题写成一个公式,那么会简单很多。
假设5个车站,只有一个车站有3颗糖,并且这3颗糖都不是当前车站的。这个时候我们能做的只能是在这个车站取一颗糖送到目的地,然后再倒回来取第二颗,直到所有的糖都送完,那么这个时候的花费是多少?
首先是第一颗糖,即使这个糖的目的地是下一个站,花费的时间是1,但是因为还有糖果没送完,所以我仍然要兜一圈回到起点,再继续送第二颗糖,只有当送最后一颗糖的时候,我才可以不用返回原车站,所以总的消费是:
n + n + dist(s, i) + dist(i, e);如果另外还有一个车站的糖果数量也是3个,那么我们要求的时间就是两个车站中运送最后一颗糖花费时间最多的那个,即 max(n + n + dist(s, i) + dist(i, e)) 。
dist(a, b)表示的是从 a 到 b 的距离,s为起点,e为终点,i 为中间点,n为火车绕一圈花费的时间。
通过上面的公式我们能看出,不管前面两个糖的运送距离有多长,我们都需要花费n的时间,我们只有让最后一颗糖花费时间尽可能的小,才能保证总花费时间是最小的。所以贪心的策略就如开始说的那样,最远的最先取,最近的最后取。
因为每到一个站都可以运送一颗糖果,所以只要我现在在运送的糖果花费的时间是n,其他站的糖果如果花费的时间也是n,或者小于n,就可以顺便完成运送任务。
通过公式,我们能很清晰的知道自己需要什么数据,首先公式中 n的个数取决于当前车站的糖果总数,如果当前车站有 cnt 颗糖,那么就有 n*(cnt-1)的消费,然后就是某车站所有糖果中运送距离最短的值 dist(i, e),因为最后总的消费时间取决于最后一颗糖,所以我们要求的就是 max(n*(cnt-1) + dist(s, i) + dist(i, e) )。
这里要注意的是,如果cnt == 0,直接套公式是会减少花费的时间的,但事实上,车站没糖,时间一样是在消耗的。
#include <iostream>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define lson (cur<<1)
#define rson (cur<<1|1)
typedef long long ll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const long long LINF = 1e18;
const int Maxn = 5e4+10;
const int Mod = 1e9+7;
int cnt[20010], minxd[5010];
int dist(int a, int b, int n) {
if(a <= b) return b-a;
else return n-(a-b);
}
int main(void)
{
int n, m;
cin >> n >> m;
int t1, t2;
memset(minxd, 0x3f, sizeof(minxd));
memset(cnt, 0, sizeof(cnt));
for(int i = 0; i < m; ++i) {
cin >> t1 >> t2;
if(t1 == t2) continue;
cnt[t1]++;
minxd[t1] = min(minxd[t1], dist(t1, t2, n));
}
for(int i = 1; i <= n; ++i) {
ll ans = -1;
for(int j = 1; j <= n; ++j) {
if(cnt[j] == 0) continue;
ans = max(ans, (ll)dist(i, j, n)+minxd[j]+n*(cnt[j]-1));
}
cout << ans << " ";
}
return 0;
}