[USACO16JAN]愤怒的奶牛Angry Cows
一道神奇的DP………(鬼知道他为什么在tarjan里面)
一开始可能会考虑贪心或者什么其他神奇的算法,不过还是DP比较靠谱。
我们用f[i]表示摧毁所有i左侧的炸 药包最少需要的能量,用g[i]表示摧毁所有i右侧的炸 药包最少需要的能量。
那么我们只要找到满足j < i,a[i] - a[j] > f[j]+1的最后一个j炸 药包,就可以更新f[i]的值,f[i] = min(f[i],a[i]-a[j],f[j]+1);
同样的g也是同理。
为什么这么找呢……因为首先我们发现如果a[i]-a[j]比f[j]+1还要小的话,那么从i点引发的爆炸是可以波及到j点的,所以并不需要更新答案,直到不满足的时候我们才更新。
最后枚举爆炸的点就可以了。
然后这题有个技巧,如果往数轴上投炸 药你要么投在点上要么投在两者中间,也就是只可能有整数或者.5的情况。这样直接把所有数据×2计算最后/2就可以了。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<queue> #include<set> #include<map> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 50005; const int INF = 2000000009; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,f[M],g[M],a[M],head,tail,ans = INF; int main() { n = read(); rep(i,1,n) a[i] = read() << 1; sort(a+1,a+1+n); n = unique(a+1,a+1+n) - a - 1; rep(i,1,n) f[i] = g[i] = INF; f[1] = -2; rep(i,2,n) { while(head + 1 < i && a[i] - a[head+1] > f[head+1] + 2) head++; f[i] = min(f[head+1] + 2,a[i] - a[head]); } g[n] = -2,tail = n; per(i,n-1,1) { while(tail - 1 > i && a[tail-1] - a[i] > g[tail-1] + 2) tail--; g[i] = min(a[tail] - a[i],g[tail-1] + 2); } //rep(i,1,n) printf("%d %d\n",f[i],g[i]); head = 1,tail = n; while(head < tail) { ans = min(ans,max((a[tail] - a[head]) >> 1,2 + max(g[tail],f[head]))); if(f[head+1] < g[tail-1]) head++; else tail--; } printf("%.1lf\n",(double)ans / 2.0); return 0; }
当你意识到,每个上一秒都成为永恒。