蓝桥杯 算法训练 试题 无聊的逗 C++ 字符串操作详解

本文介绍了一种使用C++编程解决关于n个棍子选取部分相加使两组和相等的问题,通过字符串表示选取状态并利用next_permutation生成全排列,然后优化算法以减少循环次数,提高运行效率。
摘要由CSDN通过智能技术生成
问题描述

        逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中。不过他想到了一个游戏来使他更无聊。他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的情况下长度最长是多少。

输入格式

  第一行一个数n,表示n个棍子。第二行n个数,每个数表示一根棍子的长度。

输出格式

  一个数,最大的长度。

样例输入

        4
        1 2 3 1

样例输出

        3

数据规模和约定

        n<=15

分析: 

        以通俗的话来解释题目就是:给定一列数,从中取出一部分并相加为sum1,再从剩下的数中取出另一部分相加为sum2 ,在sum1与sum2相等的所有可能情况里,问最大的sum是多少。

        我们可以用一个字符串来表示选取的状态:例如,给定一列数包含五项,我们可以用

        00000

        来表示初始状态。

        以 1 来表示组成sum1中的数在这列数中的位置,同理以 2 来表示sum2中的数在这列数中的位置。例如,我们可以用:

        10122

        来表示选取第一、第三个数相加为sum1,第四、第五个数相加为sum2。


        这样,我们就能根据字符串的状态编写一个函数,该函数将字符为1的位置所对应的数相加为sum1,字符为2的位置对应的数相加为sum2。当sum1与sum2相等时,比较sum与当前最大值记录的大小,若大于最大值,更新记录。

        该函数的实现并不困难:

int l[15]={0};
int maxsum=0;
void findmax(string ip)
{
	int sum1=0,sum2=0;
	for(int i=0;i<ip.size();++i)
		if(ip[i]=='0');
		else if(ip[i]=='1')
			sum1+=l[i];
		else 
			sum2+=l[i];
	if(sum1==sum2&&sum1>maxsum)
		maxsum=sum1;
}

        接下来的问题在于,如何生成 选取状态全排列 ,即字符串的所有可能情况。我们可以利用C++的库函数next_permutation:next_permutation在 <algorithm> 头文件中,它自动生成下一个排列直到 完全降序,这意味着我们在初始化字符串时需要以 完全升序 的方式去生成它,才能保证得到全排列的每一种情况。

        完整代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
int l[15]={0};
int maxsum=0;
void findmax(string ip)
{
	int sum1=0,sum2=0;
	for(int i=0;i<ip.size();++i)
		if(ip[i]=='0');
		else if(ip[i]=='1')
			sum1+=l[i];
		else 
			sum2+=l[i];
	if(sum1==sum2&&sum1>maxsum)
		maxsum=sum1;
}
int main()
{
	int n,k;
	cin>>n;
	for(int i=0;i<n;++i)
		cin>>l[i];
	for(int i=1;i<n;++i)
		for(int j=1;j<=n-i;++j)
		{
			k=n-i-j;         // i,j,k分别表示sum1中的数、sum2中的数以及0的个数 
			string choice(k,'0');
			string tmp1(i,'1');
			string tmp2(j,'2');
			choice+=tmp1+tmp2;
			do{
				findmax(choice);
			}
			while(next_permutation(choice.begin(),choice.end()));
		}
	cout<<maxsum;
	return 0;
}

 优化:

        虽然以上程序能成功输出正确结果,但因为大量的循环嵌套和函数调用使得运行时间随n的增大而指数级增长。

        我们首先考虑优化字符串状态:拿一开始的数列举例,不难发现,状态 10122 与状态 20211 得到的结果是一致的。因此我们可以缩小以上程序中i,即组成sum1的数的个数为 n/2。

        然后我们通过将函数归并到主程序中,最后的程序如下,成功通过所有测试点。

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int n,k,tmp,sum1,sum2;
	string choice;
	int l[15]={0};
	int maxsum=0;
	cin>>n;
	for(int i=0;i<n;++i)
		cin>>l[i];
	tmp=n/2;
	for(int i=1;i<tmp;++i)
		for(int j=1;j<=n-i;++j)
		{
			k=n-i-j;         // i,j,k分别表示sum1中的数、sum2中的数以及0的个数 
			choice="";
			for(int m=0;m<k;++m)
				choice+="0";
			for(int n=0;n<i;++n)
				choice+="1";
			for(int o=0;o<j;++o)
				choice+="2"; 
			do{
				sum1=0,sum2=0;
				for(int i=0;i<choice.size();++i)
					if(choice[i]=='0');
					else if(choice[i]=='1')
						sum1+=l[i];
					else 
						sum2+=l[i];
				if(sum1==sum2&&sum1>maxsum)
					maxsum=sum1;
			}
			while(next_permutation(choice.begin(),choice.end()));
		}
	cout<<maxsum;
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值