动态规划——某团题目 选红包

题目: 圆桌上放了一圈红包形成环形,每个红包金额不同,围绕圆桌走一圈选择若干红包,规则是不能拿相邻的红包,请问拿到红包最多的总金额是多少?
输入: 红包个数N N行数组表示N个红包
输出: 最多的总金额
样例输入:
2
1,2
1,3,4
样例输出:
2
4

在另外一篇博客中, 我们用深度优先搜索暴力破解了这个题目,但并不是最优做法,它的复杂度比较高。事实上,选红包的问题可以规约为子问题,利用动态规划来解决。而且这道题目用DP来做,无比简单。
首先要明白,桌面上既然是红包,就说明总的钱的数量只会往上增,不会下降,也就是拿的越多越好。这比背包问题简单了不少,背包问题中还有背包重量的限制。
其次,题目说红包是环形,而且不能拿相邻的,也就是首尾红包只能取其中之一。假设红包个数为N,那么原问题就分解为:求在(0,N-1)与(1,N)的红包中去取红包,最后哪边能取得钱多,哪边就是答案。
最后,最重要的,红包不能拿相邻的。考虑如果拿当前红包,总金额为F(i),它的值应该为隔它一个或者两个红包处能取得的最大值加上当前红包金额,用公式表示就是F(i)=MAX{ F(i-2),F(i-3) } + V[i]。这里不容易想通的就是为什么是i-2 与 i-3 。事实上,想一想i-4的情况,如果隔了3个了,那么这隔的3个中,中间那个实际上必定应该是拿起的,因为它只会增加总红包的金额。同理,i-5的情况也是中间必定存在可拿红包。只有i-2与i-3的情况下, 满足 中间无红包可拿 以及 跟现红包不相邻 这两个条件。
具体实现如下:

#include<stdio.h>
#include<string.h>

int maxValue=0;
int len=0;  //用来存数据的个数

int max(int a,int b)
{
    if(a <= b)
        return b;
    else
        return a;
}

int countNum(char* a,int start,int end) //此函数将数字字符串段转为数字 
{
    int result=0;
    int i=0,m=0;
    int j=start-end;
    int pownum;
    for(i=0;i<=(end-start);i++)
    {
        pownum=1;
        for(m=0;m<i;m++)
            pownum*=10;
        result+=(a[end-i]-'0') * pownum;
        j--;
    }
    return result;
}
int coverTonum(char* a,int* num)  //此函数从字符串中提取所有数字 
{
    int i=0,k=0;
    int tempi=-1; //存储找到一个逗号时的索引值
    while(*(a+i)!='\0')
    {
        if(*(a+i)==',')
        {
            num[k]=countNum(a,tempi+1,i-1);
            tempi=i;
            k++;
        }
        i++;    
    }   
    num[k]=countNum(a,tempi+1,i-1);  //最后一个逗号右边应该还有一个数 
    return k+1;
}

int DP(int* num,int *sum,int start,int end)  //动态规划
{
    int i;
    memset(sum,0,sizeof(int));
    sum[start]=num[start];
    sum[start+1]=num[start+1];
    sum[start+2]=num[start]+num[start+2];
    for(i=(start+3);i<=end;i++)
        sum[i]=max(sum[i-2],sum[i-3])+num[i];
    maxValue=sum[start];
    for(i=start;i<=end;i++)
        if(sum[i]>maxValue)
            maxValue=sum[i];
    return maxValue;
}

int main()
{
    int N,i,j;
    scanf("%d",&N);
    while(N--)
    {
        char a[100];
        int value[100];
        int sumValue[100];
        memset(value,0,sizeof(int));
        scanf("%s",a);
        len=coverTonum(a,value);

        if(len<=3) //处理输入数据只有3个及以下的特殊情况
        {
            maxValue=value[0];
            for(i=0;i<len;i++)
                if(value[i]>maxValue)
                    maxValue=value[i];
            printf("maxValue : %d\n",maxValue);         
        }
        else
            printf("maxValue : %d\n",max( DP(value,sumValue,0,len-2) , DP(value,sumValue,1,len-1) ));
    } 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值