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; }