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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值