现场赛时没有想到是dp,还以为是贪心呢,比赛结束前试着暴力dfs了一下,TLE了。
/**
题意:
一条直线上有n(1<=n<=200)个开关,开关i的属性d[i]表示它到最左端开关的距离,t[i]表示它被按下t[i]秒后又自动弹出。
求一个按开关的顺序,使得某时刻所有的开关都被按下。可以从任意一个开关开始,而且手移动的速度是每秒钟一个单位长度,
按开关所用的时间忽略不计。
题解:
dp[i][j] 表示把开关i到j都按下需要的时间,可以从任意一个开始。
现在要证明一个结论:对于区间[i,j]如果有解,那么最优解(即所花时间最短)的方案的起始点一定是在区间的端点(左端点或右端点)。
在按下第一个端点和按下另一个端点按钮过程中,必然要经过该区间内所有的点,其中有一些点已经在之前被按下,很显然,
这些点“晚按”比“早按”更优。所以,在端点被按下之前的所有按下操作都是多余的,浪费时间的,完全可以在端点按下之后再按。
(口述证明比较难懂,自己琢磨)
现在整个过程就能这样看了:开始从[1,n]区间取一个端点按下后,剩下一段连续的区间,再选取一个端点按下,还是剩下一段连续的区间...
现在就用
dp[i][j][0] 表示先按下区间[i,j]的左端点
dp[i][j][1] 表示先按下右端点
要求[1,n]就可以递归到求[1,1],[2,2]...[n,n],而dp[i,i]=0;
/**
题意:
一条直线上有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] 表示先按下右端点
要求[1,n]就可以递归到求[1,1],[2,2]...[n,n],而dp[i,i]=0;
可能递归思想更好理解一些,我写的是非递归的代码。
**/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 220;
const int INF = 0x3fffffff;
int n;
int t[N], d[N];
int dp[N][N][2], next[N][N][2];
int main()
{
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;
// 计算dp[i][j][0]
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];
next[i][j][0] = 0;
}
else
{
dp[i][j][0] = dp[i+1][j][1] + d[j] - d[i];
next[i][j][0] = 1;
}
if ( dp[i][j][0] >= t[i] || dp[i][j][0] > INF )
{
dp[i][j][0] = INF;
}
// 计算dp[i][j][1]
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];
next[i][j][1] = 0;
}
else
{
dp[i][j][1] = dp[i][j-1][1] + d[j] - d[j-1];
next[i][j][1] = 1;
}
if ( dp[i][j][1] >= t[j] || dp[i][j][1] > INF )
{
dp[i][j][1] = INF;
}
}
}
int l, r, m;
if ( dp[1][n][0] < INF )
{
l = 2;
r = n;
m = next[1][n][0];
printf("1");
}
else if ( dp[1][n][1] < INF )
{
l = 1;
r = n - 1;
m = next[1][n][1];
printf("%d", n);
}
else
{
printf("Mission Impossible\n");
continue;
}
while ( l <= r )
{
if ( m == 0 )
{
printf(" %d", l);
m = next[l][r][m];
l++;
}
else if ( m == 1 )
{
printf(" %d", r);
m = next[l][r][m];
r--;
}
}
printf("\n");
}
return 0;
}