【DP】【CF31E】 TV Game

本文介绍了一种使用反向动态规划(DP)解决序列数字分配问题的方法,目标是在给定的数字序列中,通过将数字分配给两个变量A和B,使它们的总和最大。文章详细解释了正向DP的局限性和反向DP的优势,并提供了一个具体的代码实现。

传送门

Description

给你一个长度为\(2n\)的数字,每次可以从左侧选一个数字,加入连接到一个数字\(A\)或另一个数字\(B\)后面。\(A,B\)初始为\(0\)\(A\)\(B\)必须恰好被连接\(n\)次。最大化\(A,B\)的和,输出方案

Input

第一行是\(n\),第二行是长度为\(2n\)的数字

Output

从左到右输出该数字第几位被如何处理。连接到\(A\)输出\(H\),连接到\(B\)输出\(M\)

Hint

\(1~\leq~n~\leq~18\)

Solution

看起来像是个DP啊……

考虑如果正向DP的话,每次选择一个数,数字和增加多少依赖于之前那个数字选了多少。这显然是有后效性的,难以处理。

考虑反过来DP。

这样每次选一个数只依赖于之前选了多少位,可以直接设到状态里面。于是可以设\(f_{i,j}\)\(A\)选了\(i\)位,\(B\)选了\(j\)位的数字和,转移显然。

Code

#include<cstdio>
#define rg register
#define ci const int
#define cl const long long

typedef long long int ll;

template <typename T>
inline void qr(T &x) {
    rg char ch=getchar(),lst=' ';
    while((ch > '9') || (ch < '0')) lst=ch,ch=getchar();
    while((ch >= '0') && (ch <= '9')) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(lst == '-') x=-x;
}

namespace IO {
    char buf[120];
}

template <typename T>
inline void qw(T x,const char aft,const bool pt) {
    if(x < 0) {x=-x,putchar('-');}
    rg int top=0;
    do {IO::buf[++top]=x%10+'0';} while(x/=10);
    while(top) putchar(IO::buf[top--]);
    if(pt) putchar(aft);
}

template <typename T>
inline T mmax(const T a,const T b) {return a > b ? a : b;}
template <typename T>
inline T mmin(const T a,const T b) {return a < b ? a : b;}
template <typename T>
inline T mabs(const T a) {return a < 0 ? -a : a;}

template <typename T>
inline void mswap(T &_a,T &_b) {
    T _temp=_a;_a=_b;_b=_temp;
}

const int maxn = 40;
const int INF = 0x3f3f3f3f;

int n,dn;
int MU[maxn];
ll frog[maxn][maxn],ten[maxn]={1};
char s[maxn];
bool pre[maxn][maxn];

void dfs(ci,ci);

int main() {
    qr(n);
    scanf("%s",s+1);
    dn=n<<1;
    for(rg int i=dn;i;--i) MU[dn-i+1]=s[i]-'0';
    for(rg int i=1;i<n;++i) ten[i]=ten[i-1]*10ll;
    for(rg int i=0;i<maxn;++i) for(rg int j=0;j<maxn;++j) frog[i][j]=-INF;
    frog[0][0]=0;
    for(rg int i=0;i<=n;++i) {
        for(rg int j=0;j<=n;++j) {
            int len=i+j;
            if(i) frog[i][j]=frog[i-1][j]+MU[len]*ten[i-1];
            if(j) {
                if(frog[i][j] < frog[i][j-1]+MU[len]*ten[j-1]) {
                    frog[i][j]=frog[i][j-1]+MU[len]*ten[j-1];pre[i][j]=true;
                }
            }
        }
    }
    dfs(n,n);
    putchar('\n');
    return 0;
}

void dfs(ci x,ci y) {
    if((!x) && (!y)) return;
    if(pre[x][y]) {putchar('H');dfs(x,y-1);}
    else {putchar('M');dfs(x-1,y);}
}

Summary

在序列上的线性DP,当正向难以转移时,可以考虑反向DP。

转载于:https://www.cnblogs.com/yifusuyi/p/9882755.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值