南昌邀请赛网络赛 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 
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;
}

 

你可能感兴趣的:(ACM-动态规划,ACM-日常训练)