题意:给你一个位数为 2 n 2n 2n的数字 S S S,两个初始为 0 0 0的数字 A , B A,B A,B,每次你可以将 S S S中最靠左的一位在 S S S中删除掉并接在 A A A或者 B B B其中一个的后面,要求整个操作结束后,必须 A A A与 B B B都恰好被接 n n n次,最大化 A + B A+B A+B的值,输出方案
n ≤ 18 n\leq 18 n≤18
我们考虑线性做法O(n)跑18,我们已经在A和B中放好了S的前i个数,其中A放了j个,B放了k个,那么我们对于第i+1个数,它可以放在A或B的末尾,于是我们有了状态转移方程
f [ i ] [ j ] [ k ] = m a x ( f [ i − 1 ] [ j − 1 ] [ k ] + s [ i ] ∗ 1 0 n − j ) , f [ i − 1 ] [ j ] [ k − 1 ] + s [ i ] ∗ 1 0 n − k ) f[i][j][k]=max(f[i-1][j-1][k]+s[i]*10^{n-j}),f[i-1][j][k-1]+s[i]*10^{n-k}) f[i][j][k]=max(f[i−1][j−1][k]+s[i]∗10n−j),f[i−1][j][k−1]+s[i]∗10n−k)
放数的时候注意是从最高位开始放
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
const int N=100;
int n,m,dp[N][N],a[N][N],b[N][N],c[N],len;
char go[N][N][N];
char str[N<<1];
inline int read(){
int sym=0,res=0;char ch=0;
while (!isdigit(ch))sym|=(ch=='-'),ch=getchar();
while (isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return sym?-res:res;
}
void file(){
freopen("read.in","r",stdin);
freopen("write.out","w",stdout);
}
int power(int a,int x){
int res=1;
while (x){
if (x&1)res*=a;
a*=a;x>>=1;
}return res;
}
void write(int now,int x,int y,char ch){
if (now==0){
printf("%c",ch);return;
}
if (go[now][x][y]=='H')write(now-1,x-1,y,'H');
else write(now-1,x,y-1,'M');
if (now!=len)printf("%c",ch);
}
signed main(){
n=read();len=n<<1;
scanf("%s",str+1);
for (int i=1;i<=len;i++){
c[i]=str[i]-'0';
}
for (int i=1;i<=n;i++){
a[i][i]=a[i-1][i-1]+c[i]*power(10,n-i);go[i][i][0]='H';
b[i][i]=b[i-1][i-1]+c[i]*power(10,n-i);go[i][0][i]='M';
}
for (int i=1;i<=len;i++){
for (int j=max(i-n,1ll);j<=min(i-1ll,n);j++){
int k=i-j,ta=c[i]*power(10,n-j),tb=c[i]*power(10,n-k);
if (a[i-1][j-1]+ta+b[i-1][k]>b[i-1][k-1]+tb+a[i-1][j]){
a[i][j]=a[i-1][j-1]+ta;
b[i][k]=b[i-1][k];go[i][j][k]='H';
}else{
b[i][k]=b[i-1][k-1]+tb;
a[i][j]=a[i-1][j];go[i][j][k]='M';
}
}
}
write(len,n,n,' ');
return 0;
}