原题: https://nanti.jisuanke.com/t/38223
题意:
有用火柴拼出的式子,求通过移动火柴拼出的结果的最大值。
解析:
dp[i][j]表示到了第i个数,还剩j根火柴的最大值。+号两根火柴,-号一根。枚举下一个数用的火柴数,对于加号的数,直接找最大值即可。先考虑第一位为9的情况,不行再换。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define ll long long
#define DEBU
ll used[10]={6,2,5,5,4,5,6,3,7,6};
ll wei[12]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
char x[109];
const int ff[]={6,2,5,5,4,5,6,3,7,6};
int bit[109],ct;
LL dp[109][709];
ll dfsmax(int now,int res,int digi){
if(now==1&&(digi*2>res)){
return -1e18;
}
if(now>digi) return 0;
for(int i=9;i>=0;i--){
if(res-used[i]>=2*(digi-now)){
return wei[(digi-now+1)]*i+dfsmax(now+1,res-used[i],digi);
}
}
}
ll dfsmin(int now,int res,int digi){
if(now==1&&(digi*2>res)){
return 1e18;
}
if(now>digi) return 0;
int dow=(now==1?1:0);
for(int i=dow;i<=9;i++){
if(res-used[i]>=2*(digi-now)){
return wei[(digi-now+1)]*i+dfsmin(now+1,res-used[i],digi);
}
}
}
ll fin(bool f,int wei,int ct){
if(f)
return dfsmax(1,ct,wei);
else
return dfsmin(1,ct,wei);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int len;scanf("%d",&len);
scanf("%s",x);
int sum=0;
ct=0;
int num=0;
for(int i=0;i<len;i++){
if(x[i]<='9'&&x[i]>='0')sum+=ff[x[i]-'0'],num++;
else {
bit[++ct]=num;
num=0;
if(x[i]=='-')sum++;
else sum+=2;
}
}
bit[++ct]=num;
for(int i=1;i<=ct;i++){
for(int j=0;j<=sum;j++)
dp[i][j]=-1e18;
}
for(int i=bit[1]*2;i<=bit[1]*7;i++)
if(sum-i>=0)
dp[1][sum-i]=max(dp[1][sum-i],fin(1,bit[1],i));
for(int i=2;i<=ct;i++){
int use;
int add;
for(int j=bit[i]*2;j<=bit[i]*7;j++){
use=1+j;
add=-fin(0,bit[i],j);
for(int k=use;k<=sum;k++){
if(dp[i-1][k]<-1e12)continue;
dp[i][k-use]=max(dp[i][k-use],dp[i-1][k]+add);
}
}
for(int j=bit[i]*2;j<=bit[i]*7;j++){
use=2+j;
add=fin(1,bit[i],j);
for(int k=use;k<=sum;k++){
if(dp[i-1][k]<-1e12)continue;
dp[i][k-use]=max(dp[i][k-use],dp[i-1][k]+add);
}
}
}
cout<<dp[ct][0]<<endl;
#ifdef DEBUG
for(int i=1;i<=ct;i++){
for(int j=0;j<=sum;j++){
if(dp[i][j]>-1e12){
printf("dp[%d][%d]=%lld\n",i,j,dp[i][j]);
}
}
}
#endif // DEBUG
}
return 0;
}