Zoj 3541 The Last Puzzle (dp) - 2011 ACM-ICPC Dalian Regional Contest Problem C

现场赛时没有想到是dp,还以为是贪心呢,比赛结束前试着暴力dfs了一下,TLE了。
/**
题意:
一条直线上有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;
}


你可能感兴趣的:(c)