UVa10618 - Tango Tango Insurrection(dp【复杂)

112 篇文章 0 订阅

题目链接

简介:
跳舞机

分析:
限制条件较多的一个动态规划
状态很好理解,但是限制特别多

d[i][a][b][s]表示到达第i个指示灯的时候,左脚在a,右脚在b,上一次移动的脚为s(没有移动0,左脚1,右脚2)所需的最小花费

将四个方向分别编号为上0,下3,左1,右2.
这样编码的方式有一个方便之处:求相对的方向只要和为3即可

状态转移如下:

  • 如果当前是 ’ . ‘,有三种决策
    • 选择不动
    • 左脚移到其他方向
    • 右脚移动到其他方向.
  • 如果当前是某一个方向,有两种决策
    • 左脚移动到该位置上
    • 右脚移动到该位置上.

注意不要枚举不合法的移动:
不合法的移动:

  • 左脚在右箭头上,右脚在左箭头上
  • 两脚在同一箭头上
  • 左脚在右箭头上,但是右脚要移动
  • 右脚在左箭头上,但是左脚要移动

代码中,我们采用逆推的方式

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

const int INF=0x33333333;
const int L=1;
const int R=2;
char ss[100];
int d[100][4][4][3],action[100][4][4][3];
int pos[256],n;
char place[]=".LR";

int get(int a,int ta)
{
    if (a==ta) return 3;          //相同的位置
    else if (a+ta==3) return 7;   //相对的位置
    else return 5;                //相邻的位置
}

int energy(int a,int b,int s,int f,int t,int &ta,int &tb)
//a这次的左脚位置  b这次的右脚位置  s上次的动作  f下一次要动的脚  t下一次的动作  ta左脚下次的位置  tb右脚下次的位置 
{
    ta=a; tb=b;
    if (f==1) ta=t;               //下次动作由左脚完成 
    else if (f==2) tb=t;

    if (ta==tb) return -1;        //踩到同一个位置
    if (ta==R&&tb==L) return -1;  //背向跳舞机
    if (ta==R&&tb!=b) return -1;  //a左脚在右脚的位置,但是移动了右脚,无论移动到哪儿,都是不合法的
    if (tb==L&&ta!=a) return -1;  //b右脚在左脚的位置,但是移动了右脚,无论一定到哪儿,都是不合法的

    int e=0;
    if (f==0) e=0;                //没有脚移动
    else if (f!=s) e=1;           //上一次移动的脚不是现在移动的脚
    else 
    {
        if (f==1)
           e=get(a,ta);           //移动的是左脚
        else 
           e=get(b,tb);           //移动的是右脚
    }
    return e; 
}

void update(int i,int a,int b,int s,int f,int t)
//i踩了几个箭头  a左脚位置  b右脚位置  s上次的动作  f这次要移动的脚  t移动方向 
{
    int ta,tb;
    int e=energy(a,b,s,f,t,ta,tb);

    if (e<0) return;
    int cost=d[i+1][ta][tb][f]+e;          //下次移动情况:ta tb 
    int &ans=d[i][a][b][s];

    if (ans>cost)
    {
        ans=cost;
        action[i][a][b][s]=f*4+t;          //
    }
}

void doit()
{
    n=strlen(ss);
    memset(d,0,sizeof(d));
    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) continue;

                for (int s=0;s<3;s++){                //上一次的动作,1左脚,2右脚 
                    d[i][a][b][s]=INF;
                    if (ss[i]=='.')
                    {
                        update(i,a,b,s,0,0);          //两脚都不动 

                        for (int t=0;t<4;t++)
                        {
                            update(i,a,b,s,1,t);
                            update(i,a,b,s,2,t);
                        }
                    }
                    else
                    {
                        update(i,a,b,s,1,pos[ss[i]]); 
                        update(i,a,b,s,2,pos[ss[i]]);
                    } 
                }
            }
        }
    }
}

void print()
{
    int a=1;
    int b=2;
    int s=0;
    for (int i=0;i<n;i++)
    {
        int f=action[i][a][b][s]/4;
        int t=action[i][a][b][s]%4;
        printf("%c",place[f]);

        s=f;

        if (f==1) a=t;
        else if (f==2) b=t;
    }
    printf("\n");
}

int main()
{
    pos['U']=0;
    pos['D']=3;
    pos['L']=1;
    pos['R']=2;
    while (scanf("%s",&ss)!=EOF&&ss[0]!='#')
    {
        doit();
        print();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值