Problem
题意如紫书,关于cost的计算感觉有些紫书上给的翻译不太好理解:本次无动作cost为0,本次动作和上次动作不一条腿cost为1,本次和上次动作同一条腿,没有涉及移动(只是踩了一下)cost为3,本次和上次动作同一条腿,本次为相邻移动和相对移动时cost分别为5和7。
Solution
- 条件较多,d(i,a,b,s)表示踩了前i个,当前左右脚在a,b,上次的移动状态为s时以后还会产生的最小代价。倒推,最终答案为d(0,1,2,0)。
- 代码借鉴了网上高质量的代码,详见注释。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 105,inf = 1e5,Left = 1,Right = 2;//0123 上左右下
int d[maxn][4][4][3],pos[260],action[maxn][4][4][3];
char mov[10];
string str;
int Cost(int a,int ta)//两次同一只脚动作
{
if(a==ta) return 3;//本次没有移动
if(a+ta==3) return 7;//本次移动到相对位置
return 5;//本次移动到相邻位置
}
int energy(int a,int b,int s,int f,int t,int &ta,int &tb)//f=0,1,2:本次不动,移左脚,移右脚
{
if(f==1) ta = t;
if(f==2) tb = t;
if(ta==tb) return -1;//移动之后两脚在同一位置
if(ta==Right&&tb==Left) return -1; //移动之后背对跳舞机
if(a==Right&&tb!=b||b==Left&&ta!=a) return -1;//左脚在右但移动了右脚或右脚在左但移动了左脚
int e = 0;//以下计算本次移动代价
if(f==0) e = 0;//本次不移动代价为0
else if(f!=s) e = 1;//和上次移动不是同一只脚代价为1
else if(f==1) e = Cost(a,ta);//计算与上次移动的脚相同时的代价
else e = Cost(b,tb);
return e;
}
void update(int i,int a,int b,int s,int f,int t)//移动f脚至t
{
int ta = a,tb = b;
int e = energy(a,b,s,f,t,ta,tb);
if(e==-1) return ;
int cost = d[i+1][ta][tb][f]+e;
int &ans = d[i][a][b][s];
if(ans>cost)
{
ans = cost;
action[i][a][b][s] = f*4+t;
}
}
void work()
{
int n = str.length();
memset(d,0,sizeof(d));
for(int i=n-1;i>=0;i--)
{
for(int a=0;a<=3;a++)
{
for(int b=0;b<=3;b++)
{
if(a==b) continue;
for(int s=0;s<4;s++)
{
d[i][a][b][s] = inf;
if(str[i]=='.')
{
update(i,a,b,s,0,0);//不动
for(int t=0;t<4;t++)
{
update(i,a,b,s,1,t);//左脚移动到t
update(i,a,b,s,2,t);//右脚移动到t
}
}
else{//左右脚分别一致目标
update(i,a,b,s,1,pos[str[i]]);
update(i,a,b,s,2,pos[str[i]]);
}
}
}
}
}
int a = 1,b = 2,s = 0;
for(int i=0;i<n;i++)
{
int f = action[i][a][b][s]/4,t = action[i][a][b][s]%4;
cout<<mov[f];
if(f==1) a = t;
if(f==2) b = t;
s = f;
}
cout<<endl;
}
int main()
{
// freopen("1.txt","w",stdout);
pos['U'] = 0;
pos['L'] = 1;
pos['R'] = 2;
pos['D'] = 3;
mov[0] = '.';
mov[1] = 'L';
mov[2] = 'R';
while(cin>>str&&str!="#")
{
work();
}
return 0;
}