Match Stick Game
给定一个表达式,只包含加减法,其中每个数字都是用棍子拼起来的,包括加减号(具体详见题目链接)。现在可对棍子进行重新分配。但需要保证运算符号数量不变,且参与运算的每个数的位数也不变,且其初始值<=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为参与运算的数的个数)
#include
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;
}