uva 10118 Free Candies

原题:
Little Bob is playing a game. He wants to win some candies in it - as many as possible.
There are 4 piles, each pile contains N candies. Bob is given a basket which can hold at most 5
candies. Each time, he puts a candy at the top of one pile into the basket, and if there’re two candies of the same color in it, he can take both of them outside the basket and put them into his own pocket. When the basket is full and there are no two candies of the same color, the game ends. If the game is played perfectly, the game will end with no candies left in the piles.
For example, Bob may play this game like this (N = 5):
这里写图片描述
Note that different numbers indicate different colors, there are 20 kinds of colors numbered 1..20.
‘Seems so hard…’ Bob got very much puzzled. How many pairs of candies could he take home at most?
Input
The input will contain not more than 10 test cases. Each test case begins with a line containing a single integer n(1 ≤ n ≤ 40) representing the height of the piles. In the following n lines, each line contains four integers x i 1 , x i 2 , x i 3 , x i 4 (in the range 1..20). Each integer indicates the color of the corresponding candy. The test case containing n = 0 will terminate the input, you should not give an answer to this case.
Output
Output the number of pairs of candies that the cleverest little child can take home. Print your answer in a single line for each test case.
Sample Input
5
1 2 3 4
1 5 6 7
2 3 3 3
4 9 8 6
8 7 2 1
1
1 2 3 4
3
1 2 3 4
5 6 7 8
1 2 3 4
0
Sample Output
8
0
3

中文:
给你4堆糖果,然后给你一个能容纳5个糖的篮子。每次在你篮子里面有两个相同的糖果的时候你就可以把篮子当中的这对糖果放到自己兜里,问你最多可以放兜里多少个?

#include <bits/stdc++.h>
using namespace std;
int dp[41][41][41][41],n;
int candy[41][41],ans;
bool mark[21];
bool vis[41][41][41][41];
int get_num()
{
    int cnt=0;
    for(int i=1;i<=20;i++)
        if(mark[i])
        cnt++;
    return cnt;
}
int get_candy(int r,int c)
{
    return candy[r][c];
}
int dfs(int i,int j,int k,int w)
{
    if(vis[i][j][k][w])
        return dp[i][j][k][w];
    int Max=0;
    if(get_num()<5)
    {
        if(i+1<=n)
        {
            int can=get_candy(i+1,1);
            if(mark[can])
            {
                mark[can]=0;
                Max=max(Max,dfs(i+1,j,k,w)+1);
                mark[can]=1;
            }
            else
            {
                mark[can]=1;
                Max=max(Max,dfs(i+1,j,k,w));
                mark[can]=0;
            }
        }
        if(j+1<=n)
        {
            int can=get_candy(j+1,2);
            if(mark[can])
            {
                mark[can]=0;
                Max=max(Max,dfs(i,j+1,k,w)+1);
                mark[can]=1;
            }
            else
            {
                mark[can]=1;
                Max=max(Max,dfs(i,j+1,k,w));
                mark[can]=0;
            }
        }
        if(k+1<=n)
        {
            int can=get_candy(k+1,3);
            if(mark[can])
            {
                mark[can]=0;
                Max=max(Max,dfs(i,j,k+1,w)+1);
                mark[can]=1;
            }
            else
            {
                mark[can]=1;
                Max=max(Max,dfs(i,j,k+1,w));
                mark[can]=0;
            }
        }
        if(w+1<=n)
        {
            int can=get_candy(w+1,4);
            if(mark[can])
            {
                mark[can]=0;
                Max=max(Max,dfs(i,j,k,w+1)+1);
                mark[can]=1;
            }
            else
            {
                mark[can]=1;
                Max=max(Max,dfs(i,j,k,w+1));
                mark[can]=0;
            }
        }
    }
    else
    {
        vis[i][j][k][w]=1;
        dp[i][j][k][w]=0;
        return 0;
    }
    dp[i][j][k][w]=Max;
    vis[i][j][k][w]=true;
    ans=max(ans,Max);
    return dp[i][j][k][w];
}

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n,n)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=4;j++)
                cin>>candy[i][j];
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        ans=-1;
        dfs(0,0,0,0);
        cout<<ans<<endl;
    }
    return 0;
}

解答:
这题倒是挺好想,可惜代码不是很好布置。考虑记忆化搜索,还有记录并回溯上一次的状态,这点很不好弄。
那么,一般这种题可以考虑利用引用或者指针来帮助实现。
首先先考虑搜索问题,那么一共可以搜四个状态,分别就是拿第一堆,第二堆,第三堆,第四堆最上面的。如何下来,应该是一个四叉树,那么如何优化呢?考虑,背包问题用记忆化搜索时,是避免搜索重复的子状态,而用dp(….)数组进行保存状态,那么这道题的过程当中有没有重复的子状态?有!
如图
这里写图片描述
其中,黑色的竖线代表每堆糖果,短一点的代表少了一个,如此枚举下来,肯定会有重复的状态(把绿色的圈补全就能看到),所以用dp[i][j][k][w]记录当前状态的最优值即可。题目当中还有一个要求,那就是篮子的大小是5,所以要设定一个mark数组用来记录手里有的糖果类型,注意这里的糖果类型本来应该是作为动态规划中新增加的一个维度出现的,也就是新增加的一个状态,类似dp[i][j][k][w][S]其中S代表手里的糖果种类,但是考虑到内存的原因,所以只能单独拿出来,所以在枚举状态的过程中要用到回溯来解决。篮子中糖的数量可以查看mark即可。
转移方程就是dp[i][j][k][w]=max(dp[i+1][j][k][w],dp[i][j+1][k][w],dp[i][j][k+1][w],dp[i][j][k][w+1])+2 (当前i,j,k,w状态的mark中存在第i+1,j+1,k+1,w+1同类型的糖,且篮子的数量不超过5)

  • 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、付费专栏及课程。

余额充值