題目鏈接:
題意:
給定一個表達式,只包含加減法,其中每個數字都是用棍子拼起來的,包括加減號(具體詳見題目鏈接)。現在可對棍子進行重新分配。但需要保證運算符號數量不變,且參與運算的每個數的位數也不變,且其初始值<=1e9。求重新分配後表達式結果的最大值。
思路:
先處理出棍子的總數( sum )和參與運算的每個數的位數。
設 dp[i][j]:當前處理到第 i 個參與運算的數,還有 j 根棍子可用。
對於第 i 個數,考慮兩種情況,其前面的符號爲 '+' ,則取使用now根棍子所能得到的最大值;其前面的符號爲 '-' ,則取使用now根棍子所能得到的最小值。(貪心dfs即可)
對於當前第 i 個數,使用棍子 now 根,得到的最大/最小值爲 x,則動態轉移方程:
dp[i][k-now]=max(dp[i][k-now],dp[i-1][k]+x) (其中now<=k<=sum) 。
最後 dp[cnt][0] 即爲答案(cnt爲參與運算的數的個數)
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const ll inf = 1e18;
int cost[15] = { 6,2,5,5,4,5,6,3,7,6 };
int n;
char s[120];
ll f[20];
int num[120];
ll dp[120][720];
ll dfsmax(int pos, int res, int bitnum) {
if (pos > bitnum) {
//保證恰好使用了res根棍子
if (res == 0) return 0;
return -1;
}
for (int i = 9; i >= 0; i--) {
if ((res - cost[i] >= 0) && ((res - cost[i]) >= 2 * (bitnum - pos))) {
ll x = dfsmax(pos + 1, res - cost[i], bitnum);
if (x != -1) {
ll ans = 1ll * i*f[bitnum - pos] + dfsmax(pos + 1, res - cost[i], bitnum);
return ans;
}
}
}
return -1;
}
ll dfsmin(int pos, int res, int bitnum) {
if (pos > bitnum) {
//保證恰好使用了res根棍子
if (res == 0) return 0;
return -1;
}
int now = (pos == 1) ? 1 : 0;
for (int i = now; i <= 9; i++) {
if ((res - cost[i] >= 0) && ((res - cost[i]) >= 2 * (bitnum - pos))) {
ll x = dfsmin(pos + 1, res - cost[i], bitnum);
if (x != -1) {
ll ans = 1ll * i*f[bitnum - pos] + dfsmin(pos + 1, res - cost[i], bitnum);
return ans;
}
}
}
return -1;
}
int main()
{
int T;
scanf("%d", &T);
f[0] = 1;
for (int i = 1; i <= 19; i++) {
f[i] = f[i - 1] * 10;
}
while (T--)
{
scanf("%d", &n);
scanf("%s", s);
memset(num, 0, sizeof(num));
ll sum = 0;
int len = strlen(s);
int cnt = 0, bitnum = 0;
for (int i = 0; i < len; i++) {
if (s[i] >= '0'&&s[i] <= '9') {
sum += cost[s[i] - '0'];
bitnum++;
}
else {
num[++cnt] = bitnum;
bitnum = 0;
if (s[i] == '-') sum++;
else sum += 2;
}
}
num[++cnt] = bitnum;
for (int i = 0; i <= cnt; i++) {
for (int j = 0; j <= sum; j++) {
dp[i][j] = -inf;
}
}
//合成一個數最小需要2根棍子,最多需要7根,這個區間最大700,直接遍歷即可
for (int i = 2 * num[1]; i <= 7 * num[1]; i++) {
if (sum - i >= 0) {
ll x = dfsmax(1, i, num[1]);
//x==-1表示不能用i根棍子湊出合法的數字
if (x == -1) continue;
dp[1][sum - i] = max(dp[1][sum - i], x);
}
}
for (int i = 2; i <= cnt; i++) {
for (int j = 2 * num[i]; j <= 7 * num[i]; j++) {
//+2指'+'所需棍子數
int now = j + 2;
if (sum - now < 0) break;
ll x = dfsmax(1, j, num[i]);
if (x == -1) continue;
for (int k = now; k <= sum; k++) {
if (dp[i - 1][k] != -inf)
dp[i][k - now] = max(dp[i][k - now], dp[i - 1][k] + x);
}
}
for (int j = 2 * num[i]; j <= 7 * num[i]; j++) {
//+1指'-'所需棍子數
int now = j + 1;
if (sum - now < 0) break;
ll x = dfsmin(1, j, num[i]);
if (x == -1) continue;
//由於前面是減號,所以需要*-1
x *= -1;
for (int k = now; k <= sum; k++) {
if (dp[i - 1][k] != -inf)
dp[i][k - now] = max(dp[i][k - now], dp[i - 1][k] + x);
}
}
}
printf("%lld\n", dp[cnt][0]);
}
return 0;
}