UVa10618

来源:互联网 发布:docker github 源码 编辑:程序博客网 时间:2024/06/05 07:46

题目链接

简介:
跳舞机

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

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;}
原创粉丝点击