题目: 圆桌上放了一圈红包形成环形,每个红包金额不同,围绕圆桌走一圈选择若干红包,规则是不能拿相邻的红包,请问拿到红包最多的总金额是多少?
输入: 红包个数N N行数组表示N个红包
输出: 最多的总金额
样例输入:
2
1,2
1,3,4
样例输出:
2
4
这道题有两个坎。
- 要将输入的数据给找出来。解决办法是,把输入的全部当场字符串存储,再在里面根据逗号,进行数据提取,最后存到数组里。
- 接下来就是选红包。第一反应就是深度优先搜索暴力破解。所谓的不能选相邻的红包,也就是一个剪枝的问题。若选择拿当前红包,那么拿下一个红包至少得隔一个;如果不选择当前红包,那么下一个红包就可以直接拿相邻的。
具体的见如下代码:
#include<stdio.h>
#include<string.h>
int maxValue;
int len=0; //用来存数据的个数
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;
}
void DFS(int* num,int* vis,int n,int value) //vis数组主要用来标记哪些红包被选中
{
int i;
if(value>maxValue)
maxValue=value;
if(n>len)
return ;
else
{
vis[n]=1;
if(vis[0]==1 && vis[len-1]==1) //剪枝,同时选中的情况不考虑
return ;
DFS(num,vis,n+2,value+num[n]);
vis[n]=0;
DFS(num,vis,n+1,value);
}
}
int main()
{
int N,i;
scanf("%d",&N);
while(N--)
{
char a[100];
int num[100];
int vis[100];
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
scanf("%s",a);
len=coverTonum(a,num);
DFS(num,vis,0,0);
printf("maxValue : %d\n",maxValue);
}
}
除了以上方式,还有两种思路:
- 首尾红包不能同时拿,那么最大值就是 num[0]~num[n-2] 与 num[1]~num[n-1] 两者之间取红包的最大值,仍然可用深搜破解。
- 用动态规划思想。如果取第j个红包,获得的价值最大,记为Func(j),那么红包最大价值Func(j)就可以分解为隔它一个或者隔它两个红包处取得的最优解加上num[j]的价值。用公式表示就是:Func(j)=Max{ Func(j-2),Func(j-3) }+num[j] 。具体代码,待以后实现。