UVA10618 多阶段DP

题意

跳舞机,需要按给出的方向序列踩箭头。一个箭头可以由左脚踩,也可以由右脚踩。正常情况下,不能出现左脚在右,右脚在左的情况。但是,可以出现,左脚在右,右脚在上或在下的情况,此时,在左脚移动前,右脚不能移动。当出现箭头时,即便你的脚在这个箭头上,也需要踩一下。如果这只脚上个时间单位没有任何动作,则消耗1单位能量。如果这只脚上个时间单位没有移动,则消耗3单位能量。如果这只脚上个时间单位移动到相邻箭头,消耗5单位能量。如果这只脚上个单位移动到相对箭头,消耗7单位能量。求如何踩所消耗的能量最少。

题解

多阶段DP。用dp[i][a][b][s]代表已经踩了i个箭头,左脚在a箭头上,右脚在b箭头上,上一个阶段移动的脚为s(0代表没有脚移动,1代表左脚移动,2代表右脚移动)。如果下一步是’.’则有三种决策方案,左脚移动到另一个箭头,右脚移动到另一个箭头,不移动。如果下一步是四个箭头之一,则有两种决策方案,左脚移动到该箭头,右脚移动到该箭头。

注意事项

本题条件判断比较多,特别要注意判断是否相等的等号(==)不要写成=。

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
int dp[100][4][4][3];
const int LEFT=1,RIGHT=2;
char foot[]=".LR";
char chc[256];

struct Node{
    int f,t;
};
Node action[100][4][4][3];


int getAdd(int a,int b){
    if(a==b)
        return 3;
    else if(a+b==3)
        return 7;
    else
        return 5;
}

int update(int i,int a,int b,int s,int t,int f,int &ta,int &tb){
    ta=a,tb=b;
    if(f==LEFT){
        ta=t;
    }else if(f==RIGHT){
        tb=t;
    }

    if(ta==tb){
        return -1;
    }else if(ta==RIGHT&&tb==LEFT){
        return -1;
    }else if(b==LEFT&&ta!=a){
        return -1;
    }else if(a==RIGHT&&tb!=b){
        return -1;
    }

    if(f==0){
        return 0;
    }else if(f!=s){
        return 1;
    }else{
        if(f==LEFT){
            return getAdd(a,ta);
        }else{
            return getAdd(b,tb);
        }
    }
}

void energy(int i,int a,int b,int s,int t,int f){
    int ta,tb;
    int v=update(i,a,b,s,t,f,ta,tb);
    if(v<0)
        return ;

    int& ans=dp[i][a][b][s];
    if(ans>v+dp[i+1][ta][tb][f]){
        ans=v+dp[i+1][ta][tb][f];
        action[i][a][b][s]=(Node){f,t};
    }
}

int main()
{
    //freopen("d://input.txt","r",stdin);
    //freopen("d://output.txt","w",stdout);
    char ch[105];
    chc['U']=0,chc['L']=1,chc['R']=2,chc['D']=3;
    while(scanf("%s",ch)){
        if(ch[0]=='#')
            break;
        int n=strlen(ch);
        memset(dp,0,sizeof(dp));
        for(int i=n-1;i>=0;i--){
            for(int a=0;a<4;a++){
                for(int b=0;b<4;b++){
                    if(a!=b){
                        for(int s=0;s<3;s++){
                            dp[i][a][b][s]=n*10;
                            if(ch[i]=='.'){
                                energy(i,a,b,s,0,0);
                                for(int k=0;k<4;k++){
                                    energy(i,a,b,s,k,LEFT);
                                    energy(i,a,b,s,k,RIGHT);
                                }
                            }else{
                                energy(i,a,b,s,chc[ch[i]],LEFT);
                                energy(i,a,b,s,chc[ch[i]],RIGHT);
                            }
                        }
                    }
                }
            }
        }

        int a=LEFT,b=RIGHT,s=0;
        for(int i=0;i<n;i++){
            int f=action[i][a][b][s].f;
            printf("%c",foot[f]);
            int t=action[i][a][b][s].t;
            s=f;
            if(f==LEFT){
                a=t;
            }else if(f==RIGHT){
                b=t;
            }
        }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值