P2466 [SDOI2008]Sue的小球(区间dp好题,关路灯类似)
题目大意:海面(一条线)上空漂浮着许多石头,有一定的价值,而且它们会慢慢降落,而价值随着降落的高度增大而减小,甚至为负数
我们从某个位置出发,以一定的速度去把所有石头都收集起来,问最多能获得的价值。
思路:
我虽然之前做过关路灯,但是还是没有想到哪里去,真的是惭愧啊.思路是和关路灯类似的
而这道题与关路灯有点稍微不一样的地方就是这道题起点不在节点中,而关路灯是起点就在节点中的
关路灯的初始化只需要把起点位置置为0即可开始dp,这道题呢,方法1是加入一个节点,x设置为起点,然后排序后依然
像关路灯一样找到起点初始化为0,方法2是直接用起点对其余点的长度为1的区间初始化,再去dp,也能得到最优解
就是f1[i][j],f2[i][j],表示i-j区间走完后所有点下降的最小高度和,1/2分别表示停止在左右那个端点,
转移方程:
f1[i][j]=min(f1[i+1][j]+(a[i+1].x-a[i].x)*(a[n].v-a[j].v+a[i].v),f2[i+1][j]+(a[j].x-a[i].x)*(a[n].v-a[j-1].v+a[i].v));
f2[i][j]=min(f2[i][j-1]+(a[j].x-a[j-1].x)*(a[n].v-a[j-1].v+a[i-1].v),f1[i][j-1]+(a[j].x-a[i].x)*(a[n].v-a[j-1].v+a[i-1].v));
初始化f1[i][i]=f2[i][i]=abs(s-a[i].x)*a[n].v;
这里我把物品的速度用前缀和a[i]表示,这真是一道好题啊
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1007; ll n, s, ans; ll f1[maxn][maxn], f2[maxn][maxn]; struct node { int x, y; ll v; bool operator<(node b)const { return x < b.x; } }a[maxn]; int main() { //freopen("test.txt", "r", stdin); scanf("%lld%lld", &n, &s); memset(f1, 0x3f, sizeof(f1)); memset(f2, 0x3f, sizeof(f2)); for (int i = 1; i <= n; i++) { scanf("%lld", &a[i].x); } for (int i = 1; i <= n; i++) { scanf("%lld", &a[i].y); ans += a[i].y; } for (int i = 1; i <= n; i++) { scanf("%lld", &a[i].v); } sort(a + 1, a + n + 1); for (int i = 1; i <= n; i++) { a[i].v += a[i-1].v; } for (int i = 1; i <= n; i++) { f1[i][i] = f2[i][i] = abs(s - a[i].x) * (a[n].v);//初始化长度为1的情况 } for (int len = 2; len <= n; len++) { for (int i = 1, j = i + len - 1; j <= n; i++, j++) { //可能从两端转移过来 f1[i][j] = min(f1[i + 1][j] + (a[i + 1].x - a[i].x) * (a[n].v - a[j].v + a[i].v), f2[i + 1][j] + (a[j].x - a[i].x) * (a[n].v - a[j].v + a[i].v)); f2[i][j] = min(f2[i][j - 1] + (a[j].x - a[j - 1].x) * (a[n].v - a[j - 1].v + a[i - 1].v), f1[i][j - 1] + (a[j].x - a[i].x) * (a[n].v - a[j - 1].v + a[i - 1].v)); } } printf("%.3lf", (ans - min(f1[1][n], f2[1][n]))/1000.0); return 0; }
关路灯代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 55; ll n, c; ll a[maxn], b[maxn]; ll f1[maxn][maxn], f2[maxn][maxn]; int main() { //freopen("test.txt", "r", stdin); memset(f1, 0x3f, sizeof(f1)); memset(f2, 0x3f, sizeof(f2)); scanf("%lld%lld", &n, &c); for (int i = 1; i <= n; i++) { scanf("%lld%lld", &a[i], &b[i]); b[i] += b[i - 1]; } f1[c][c] = f2[c][c] = 0;//只初始化起点 for (int len = 2; len <= n; len++) { for (int i = 1, j = i + len - 1; j <= n; i++,j++) { //同样的dp转移方程 f1[i][j] = min(f1[i + 1][j] + (a[i + 1] - a[i]) * (b[n] - b[j] + b[i]), f2[i+1][j] + (a[j] - a[i]) * (b[n] - b[j] + b[i])); f2[i][j] = min(f2[i][j - 1] + (a[j] - a[j - 1]) * (b[n] - b[j - 1] + b[i - 1]), f1[i][j-1]+(a[j]-a[i])*(b[n]-b[j-1]+b[i-1])); } } cout << min(f1[1][n], f2[1][n]) << endl; return 0; }