南昌邀請賽網絡賽 D. Match Stick Game(dp)

題目鏈接:

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爲參與運算的數的個數)

 

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章