题目链接:哆啦A梦传送门
题意:给出 0~9 十个数字,每个数字都由一些数量的火柴棒构出,现在给你一个表达式,你可以移动一些火柴棒使得这个表达式的值最大,但是要保证新的表达式的操作符与每个数字的位数要和原来的一模一样,最后输出最大表达式的值。
题解:参考博客:神犇
我只是加了点注释,方便自己以后回顾。
先预处理:
val[i][j][0] 表示位数为i,火柴棒为j的最小值
val[i][j][1] 表示位数为i,火柴棒为j的最大值
接着设 dp[i][j]表示前i个数字剩余j条火柴棒数量的最大值。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
///val[i][j][0] 表示位数为i,火柴棒为j的最小值
///val[i][j][1] 表示位数为i,火柴棒为j的最大值
LL val[15][75][2];
///dp[i][j]表示前i个,剩余火柴棒为j的最大值
LL dp[110][710];
int num[10]={6,2,5,5,4,5,6,3,7,6};
void init()
{
for(int i=0;i<=12;i++)
for(int j=0;j<=72;j++)
val[i][j][1]=-1e10,val[i][j][0]=INF;
val[0][0][0]=val[0][0][1]=0;
for(int i=1;i<=10;i++)///位数
{
///i*7是因为一位数最多有7条火柴棒
for(int j=1;j<=i*7;j++) ///可使用的火柴棒数
{
for(int k=0;k<=9;k++){ ///变换的数字
if(j<num[k]) continue;
val[i][j][0]=min(val[i][j][0],val[i-1][j-num[k]][0]*10+k);
val[i][j][1]=max(val[i][j][1],val[i-1][j-num[k]][1]*10+k);
}
}
}
}
char op[110];
///存储每个操作符op前面的数字的位数
int pre[110],cnt;///cnt表示有多少个数字
int main()
{
int ncase,n;
cin>>ncase;
init();
while(ncase--)
{
cin>>n>>op;
// cout<<op<<endl;
int len=strlen(op);
///last记录前一个字符的位置
int last=-1,tot=0;
cnt=0;
for(int i=0;i<len;i++)
{
if(op[i]=='+'||op[i]=='-'){
pre[++cnt]=i-last-1;
last=i;
}
if(op[i]=='+') tot+=2;
else if(op[i]=='-') tot++;
else tot+=num[op[i]-'0'];
}
pre[++cnt]=len-last-1;
// printf("tot=%d\n",tot);
for(int i=0;i<=n;i++)
for(int j=0;j<=n*7;j++)
dp[i][j]=-1e12;
int item=min(pre[1]*7,tot);
for(int i=1;i<=item;i++){
dp[1][tot-i]=max(dp[1][tot-i],val[pre[1]][i][1]);
// printf("%lld\n",dp[1][tot-i]);
}
///第几个数字
for(int i=2;i<=cnt;i++)
{
///枚举火柴棒的数量
for(int j=0;j<=tot;j++)
{
item=min(pre[i]*7+2,j);
for(int k=2;k<=item;k++)///可使用的火柴棒数
{
///要么减去最小值,要么加上最大值
dp[i][j-k]=max(dp[i][j-k],dp[i-1][j]-val[pre[i]][k-1][0]);
dp[i][j-k]=max(dp[i][j-k],dp[i-1][j]+val[pre[i]][k-2][1]);
}
}
}
printf("%lld\n",dp[cnt][0]);
}
return 0;
}
/*
tot=5
-10000000000
1
7
4
5
*/