UVa 10118 Free Candies

题目链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1059


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

// record[a][b][c][d]代表第1,2,3,4堆取了a,b,c,d个糖后(位于顶部的糖果为pile[0][a+1],pile[1][b+1],pile[2][c+1], pile[3][d+1]),
// 篮子里的情况是basket[a][b][c][d]时还能取的最多糖对
int record[45][45][45][45];

// basket[a][b][c][d]代表第1,2,3,4堆取了a,b,c,d个糖后,篮子里的糖果情况,第i位代表第i种糖数量
int basket[45][45][45][45];

// pile[a][x]代表第a堆的第x个糖果的种类
int pile[4][45];

int n;

int get_max(int* now);

int main()
{
	while(scanf("%d", &n) == 1 && n != 0)
	{
		// 读入情况
		memset(record, -1, sizeof(record));
		memset(basket, 0, sizeof(basket));
	
		for(int i = 1; i <= n; i++)
			for(int j = 0; j <= 3; j++)
				scanf("%d", &pile[j][i]);
			
		// 计算结果
		int now[4];
		memset(now, 0, sizeof(now));
		printf("%d\n", get_max(now));			
	}
	return 0;
}

// 计算
// record[a][b][c][d]代表第1,2,3,4堆取了a,b,c,d个糖后(位于顶部的糖果为pile[0][a+1],pile[1][b+1],pile[2][c+1], pile[3][d+1]),
// 篮子里的情况是basket[a][b][c][d]时还能取的最多糖对
int get_max(int* now)
{
	if(record[now[0]][now[1]][now[2]][now[3]] != -1)
		return record[now[0]][now[1]][now[2]][now[3]];
	
	// 查看篮子中的糖果情况
	int b = basket[now[0]][now[1]][now[2]][now[3]];

	int candy[21];
	memset(candy, 0, sizeof(candy));
	int sum = 0;
	for(int i = 1; i <= 20; i++)
	{
		candy[21-i] = b - ((b >> 1) << 1);
		b = (b >> 1);
		sum += candy[21-i];	
	}
	if(sum >= 5)
	{
		record[now[0]][now[1]][now[2]][now[3]] = 0;
		return record[now[0]][now[1]][now[2]][now[3]];
	}
	
	// 如果篮子有空,查看四种可能的取法
	int ans = 0;
	for(int i = 0; i < 4; i++)
	{
		if(now[i]+1 > n)
			continue;
		int new_c = pile[i][now[i]+1];
		now[i]++;
		if(candy[new_c] == 0)
		{
			candy[new_c] = 1;
			int this_b = 0;
			for(int i = 1; i <= 20; i++)
				this_b = (this_b<<1) + candy[i];
			basket[now[0]][now[1]][now[2]][now[3]] = this_b;
			ans = max(ans, get_max(now));	
			candy[new_c] = 0;
		}		
		else if(candy[new_c] == 1)
		{
			candy[new_c] = 0;
			int this_b = 0;
                        for(int i = 1; i <= 20; i++)
                                this_b = (this_b<<1) + candy[i];
                        basket[now[0]][now[1]][now[2]][now[3]] = this_b;
                        ans = max(ans, get_max(now) + 1);   
                        candy[new_c] = 1;		
		}
		now[i]--;
	}		
	record[now[0]][now[1]][now[2]][now[3]] = ans;
	return record[now[0]][now[1]][now[2]][now[3]];	
}

感觉是道好题,题目状态一开始比较好想,无非是四个堆的要取的位置和篮子里的情况,篮子里的情况可以用一个整数来二进制表示。

但是即使这样,存储的数组也太大((40^4)*(2^20)). 后来没想出来,看了别人的做法,发现如果能够达到目前堆的状态,那么篮子里的情况应该是唯一的。(不知如何证明)

所以只需要保存堆的状态就可以了,用另外一个数组来记录每个堆的状态下篮子的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段程序的目的是计算将糖果均分给两个人所需的最小操作次数。让我们来分析一下为什么输入47会得到7作为结果。 当输入为47时,程序通过递归调用 `divide(candies, count, minCount)` 进行计算。初始调用是 `divide(47, 0, minCount)`。 首先,程序检查是否只剩下一个糖果。由于47不等于1,所以不满足条件。 接下来,程序检查47是否为偶数。由于47不是偶数,所以执行 `else` 分支。 在 `else` 分支中,程序进行了两个递归调用: 1. `divide(candies + 1, count + 1, minCount)`:这是将糖果数量加1的操作,并将操作次数加1。 2. `divide(candies - 1, count + 1, minCount)`:这是将糖果数量减1的操作,并将操作次数加1。 这两个递归调用会产生分支,并继续递归地进行计算。 对于第一个递归调用 `divide(candies + 1, count + 1, minCount)`,它会将糖果数量从47增加到48,并将操作次数从0增加到1。 接着,程序继续递归调用 `divide(candies // 2, count + 1, minCount)`,此时糖果数量为48。由于48是偶数,程序执行 `divide(candies // 2, count + 1, minCount)`,将糖果数量除以2,并将操作次数加1。 然后,程序继续递归调用 `divide(candies // 2, count + 1, minCount)`,此时糖果数量为24。同样地,程序将糖果数量除以2,并将操作次数加1。 接下来,程序继续递归调用 `divide(candies // 2, count + 1, minCount)`,此时糖果数量为12。同样地,程序将糖果数量除以2,并将操作次数加1。 继续递归调用 `divide(candies // 2, count + 1, minCount)`,此时糖果数量为6。同样地,程序将糖果数量除以2,并将操作次数加1。 接下来,程序继续递归调用 `divide(candies // 2, count + 1, minCount)`,此时糖果数量为3。由于3是奇数,程序将糖果数量加1,并将操作次数加1。 然后,程序继续递归调用 `divide(candies // 2, count + 1, minCount)`,此时糖果数量为4。同样地,程序将糖果数量除以2,并将操作次数加1。 最后,程序继续递归调用 `divide(candies // 2, count + 1, minCount)`,此时糖果数量为2。同样地,程序将糖果数量除以2,并将操作次数加1。 此时,糖果数量变为1,满足终止条件。程序将当前的操作次数1与 `minCount[0]` 中的值进行比较,并将较小值更新到 `minCount[0]` 中。 综上所述,最小操作次数为7。因此,输入47得到的结果是7。 如果你有任何其他问题,请告诉我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值