ACM-ICPC Live Archive 2031 Dance Dance Revolution

动态规划

黑书的例题,老题了,2000年的国赛题,题意看黑书吧,太长了。。。比较典型的按阶段性决策,整个dp还是不难想的,1A

分析在代码中

/*
按时间决策的DP,或者说按阶段性决策
当前要跳的格子出现了,那么怎么跳其实只有两种选择,用左脚去踩或者右脚去踩
所以要枚举前一个格子结束的时候,左右脚在什么地方,如果用左脚踩会产生多少花费,用右脚踩会产生多少花费
dp[i][l][r]表示踩完第i次,左脚在l这个格子上,右脚在r这个格子上的最小花费
那么最终要找的答案在dp[n][l][r]中,所以要扫描一次dp[n]找到最大值
由题目的性质就知道可以使用滚动数组,而且题目也没提到序列的长度,所以用滚动数组也更为保险
*/
#include <cstdio>
#include <cstring>
#define min(a,b) a<b?a:b
#define INF 0x3f3f3f3f

long long dp[2][5][5]; //滚动数组,dp[0]上一次,dp[1]这一次

int cal(int x ,int s)
{
    int cost;
    if(x==0) cost=2; //从中心到任何位置都是2
    else if(x==s) cost=1; //原地踩为1
    else if((x==1 && s==3)||(x==3 && s==1)||(x==2 && s==4)||(x==4 && s==2))
        cost=4; //移动到对面
    else //相邻的移动
        cost=3;
    return cost;
}

int main()
{
    int s;
    while(scanf("%d",&s)!=EOF && s)
    {
        memset(dp,0x3f,sizeof(dp));
        //一开始两个脚都在中心
        dp[0][s][0]=2; //用左脚去踩第一个
        dp[0][0][s]=2; //用右脚去踩第一个
        while(scanf("%d",&s) && s)
        {
            for(int l=0; l<5; l++) //初始化
                for(int r=0; r<5; r++)
                    dp[1][l][r]=INF;
            //用左脚去踩,那么右脚和之前的位置是相同的
            //右脚不动,那么先枚举右脚的位置,但是右脚不能站在当前要踩的位置
            //再枚举左脚之前站的位置,左脚之前站的位置与右脚位置也不能相同
            for(int r=0; r<5; r++)
                if(r!=s) //枚举右脚,但是右脚不能与当前位置相同
                    for(int l=0; l<5; l++)
                        if(l!=r) //左脚和右脚位置不相同
                        {
                            int cost=cal(l,s); //计算这样移动的花费
                            dp[1][s][r] = min(dp[1][s][r] , dp[0][l][r]+cost);
                        }
            //同样地用右脚去踩,那么左脚和之间的位置是相同的
            //左脚不动,那么先枚举左脚的位置,但是左脚不能与当前要踩的格子相同
            //再枚举右脚之前站的位置,但是右脚的位置不能和左脚位置相同
            for(int l=0; l<5; l++) //先枚举左脚
                if(l!=s)
                    for(int r=0; r<5; r++) //再枚举右脚
                        if(r!=l) //左右脚位置不同
                        {
                            int cost=cal(r,s);
                            dp[1][l][s] = min(dp[1][l][s] , dp[0][l][r]+cost);
                        }

            //滚动数组,复制过去
            for(int l=0; l<5; l++)
                for(int r=0; r<5; r++)
                    dp[0][l][r]=dp[1][l][r];
        }

        long long ans=INF;
        for(int l=0; l<5; l++)
            for(int r=0; r<5; r++)
                ans = min(ans,dp[0][l][r]);
        printf("%lld\n",ans);
    }
    return 0;
}

 

 

posted @ 2013-03-23 23:08  Titanium  阅读(317)  评论(0编辑  收藏  举报