问题描述
逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中。不过他想到了一个游戏来使他更无聊。他拿出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;
}