• 【DP】【CF31E】 TV Game


    传送门

    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('
    ');
    	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。

  • 相关阅读:
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    set IDENTITY_INSERT on 和 off 的设置
    导入本地Excel到DataSet中
    SQL结果统计 GROUP BY
    算法:10幢房子分给3个人
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9882755.html
Copyright © 2020-2023  润新知