zoj3541(区间dp)

来源:互联网 发布:源码资本办公地址 编辑:程序博客网 时间:2024/06/18 05:35

这题要知道是dp也有很大难度,证明我也不会,参考:大牛博客

题意:
一条直线上有n(1<=n<=200)个开关,开关i的属性d[i]表示它到最左端开关的距离,t[i]表示它被按下t[i]秒后又自动弹出。
求一个按开关的顺序,使得某时刻所有的开关都被按下。可以从任意一个开关开始,而且手移动的速度是每秒钟一个单位长度,
按开关所用的时间忽略不计。

思路:
dp[i][j] 表示把开关i到j都按下需要的时间,可以从任意一个开始。
现在要证明一个结论:对于区间[i,j]如果有解,那么最优解(即所花时间最短)的方案的起始点一定是在区间的端点(左端点或右端点)。

证明:

对于n<=2显然成立。

对于n>=3,假设起始点不是端点,而是中间的某个点,在按完一些按钮后必然会按一个端点按钮(此时另一个端点还没有被按下),
在按下第一个端点和按下另一个端点按钮过程中,必然要经过该区间内所有的点,其中有一些点已经在之前被按下,很显然,
这些点“晚按”比“早按”更优。所以,在端点被按下之前的所有按下操作都是多余的,浪费时间的,完全可以在端点按下之后再按。
(口述证明比较难懂,自己琢磨)
现在整个过程就能这样看了:开始从[1,n]区间取一个端点按下后,剩下一段连续的区间,再选取一个端点按下,还是剩下一段连续的区间...
现在就用

dp[i][j][0] 表示先按下区间[i,j]的左端点
dp[i][j][1] 表示先按下右端点

/*-------------------------------------------------------------------------------区间dp-------------------------------------------------------------------------------*/#include<iostream>#include<algorithm>#include<string.h>#include<stdio.h>#include<math.h>using namespace std;typedef long long lld;typedef unsigned int ud;#define oo 0x3f3f3f3f#define maxn 205int dp[maxn][maxn][2];int path[maxn][maxn][2];int t[maxn], d[maxn];int main(){int n;while (scanf("%d", &n) != EOF){for (int i = 1; i <= n; i++)scanf("%d", &t[i]);for (int i = 1; i <= n; i++)scanf("%d", &d[i]);memset(dp, 0, sizeof dp);for (int L = 2; L <= n; L++)for (int i = 1; i + L - 1<= n; i++){int j = i + L - 1;if (dp[i + 1][j][0] + d[i + 1] - d[i] < dp[i + 1][j][1] + d[j] - d[i]){dp[i][j][0] = dp[i + 1][j][0] + d[i + 1] - d[i];path[i][j][0] = 0;}else{dp[i][j][0] = dp[i + 1][j][1] + d[j] - d[i];path[i][j][0] = 1;}if (dp[i][j][0] >= t[i] || dp[i][j][0]>oo){dp[i][j][0] = oo;}if (dp[i][j - 1][0] + d[j] - d[i] < dp[i][j - 1][1] + d[j] - d[j - 1]){dp[i][j][1] = dp[i][j - 1][0] + d[j] - d[i];path[i][j][1] = 0;}else{dp[i][j][1] = dp[i][j - 1][1] + d[j] - d[j - 1];path[i][j][1] = 1;}if (dp[i][j][1] >= t[j] || dp[i][j][1]>oo){dp[i][j][1] = oo;}}int L, R, m;if (dp[1][n][0] < oo){printf("1");L = 2;R = n;m = path[1][n][0];}else if (dp[1][n][1] < oo){printf("%d", n);L = 1;R = n - 1;m = path[1][n][1];}else{printf("Mission Impossible\n");continue;}while (L <= R){if (m == 0){printf(" %d", L);m = path[L][R][m];L++;}else{printf(" %d", R);m = path[L][R][m];R--;}}puts("");}return 0;}


0 0