100道动态规划——15 UVA 10618 Tango Tango Insurrection 复杂的转移方程。。

好吧,这道题我也写了挺久的,一个是题意的缘故,还有一个就是状态转移方程写的不是好。。

状态的定义其实很好找,就是dp[i][lef][rif][dir]表示上次动的腿是dir,现在左脚位于lef,右脚位于rif,跳到第i个音符的最小值。

不是很明白为什么会想到反着定义。。或许是因为经验不足的缘故,反着定义的话,打印解得时候就不是递归了,而是迭代。

顺带复习了一下C++11的新姿势tuple,好玩就用了一下,用来重构解。

关键的状态转移方程,也就是要确定前后状态的变化,找到前后之间的关系,总之,学到了写复杂转移方程的姿势。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <tuple>
#include <cmath>

using std::tuple;
using std::ios_base;
using std::cout;
using std::cin;
using std::make_tuple;
using std::get;
using std::endl;

using tu=tuple<int,int,int>;//the same as typedef tuple<int,int,int> tu;
enum {left,down,right,up};

const char* ans= ".LR";
char str[75],pos[128];
int dp[75][4][4][3];
tu opt[75][4][4][3];
void update(int i,int lef,int rif,int dir,int f,int aim),print(int i,const tu&);
int calc(int i,int lef,int rif,int dir,int f,int aim,int& ta,int& tb),calc(int a,int ta);

int main(){
    ios_base::sync_with_stdio(false);
    pos['L']=left,pos['R']=right,pos['U']=up,pos['D']=down;
    while(cin>>str&&str[0]!='#'){
        int len=strlen(str);
        memset(dp,0x3f,sizeof dp);
        dp[0][left][right][0]=0;

        for(int i=0;str[i];++i)
        for(int lef=left;lef<=up;++lef)
        for(int rif=left;rif<=up;++rif)
        if(lef!=rif)
        for(int dir=0;dir<3;++dir)
        if(dp[i][lef][rif][dir]!=0x3f3f3f3f)
        if(str[i]=='.'){
        update(i,lef,rif,dir,0,0);
        for(int k=left;k<=up;++k)
            update(i,lef,rif,dir,1,k),update(i,lef,rif,dir,2,k);
        }
        else{
            update(i,lef,rif,dir,1,pos[str[i]]);
            update(i,lef,rif,dir,2,pos[str[i]]);
        }

        int ans=0x3f3f3f3f,le,ri,di;

        for(int lef=left;lef<=up;++lef)
        for(int rif=left;rif<=up;++rif)
        if(lef!=rif)
        for(int dir=0;dir<3;++dir)
        if(ans>dp[len][lef][rif][dir])
            ans=dp[len][le=lef][ri=rif][di=dir];

        print(len+1,make_tuple(le,ri,di));
        cout<<endl;
    }
    return 0;
}

void update(int i,int lef,int rif,int dir,int f,int aim){
    int ta,tb,te=calc(i,lef,rif,dir,f,aim,ta,tb);
    if(te<0)
        return;


    if(dp[i+1][ta][tb][f]>te+dp[i][lef][rif][dir])
        dp[i+1][ta][tb][f]=te+dp[i][lef][rif][dir],opt[i+1][ta][tb][f]=make_tuple(lef,rif,dir);
}

void print(int i,const tu& t){
    if(i==1)
        return;
    print(i-1,opt[i-1][get<0>(t)][get<1>(t)][get<2>(t)]);
    cout<<ans[get<2>(t)];
}
int calc(int i,int lef,int rif,int dir,int f,int aim,int& ta,int& tb){
    ta=lef,tb=rif;
    if(1==f)ta=aim;
    else if(2==f)tb=aim;

    //cout<<i<<' '<<ta<<' '<<tb<<' '<<lef<<' '<<rif<<' '<<f<<' '<<aim<<endl;
    if(ta==tb)return -1;
    else if(ta==right&&tb==left)return -1;
    else if(lef==right&&tb!=rif)return -1;
    else if(rif==left&&ta!=lef)return -1;

    if(0==f)
        return 0;
    else if(f!=dir)
        return 1;
    else
        return (f==1)?calc(lef,ta):calc(rif,tb);
}

int calc(int a,int ta){
    if(a==ta)
        return 3;
    else if(abs(ta-a)==2)
        return 7;
    else
        return 5;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值