记录撰写以及调试HDU2062相关代码的感悟。
首先,明确下解题思路。
显然,这题不能记录An所有的子序列。当n=20时,子序列个数超过10亿级别啦。
如下解释来自网络:(修改了一点)
A2={1,2},子序列为:{1} {1,2}
{2} {2,1}
A3={1,2,3},子序列为: {1} {1,2} {1,2,3} {1,3} {1,3,2}
{2} {2,1} {2,1,3} {2,3} {2,3,1}
{3} {3,1} {3,1,2} {3,2} {3,2,1}
不难发现,An可以按首数字分成n组,而每组里除了第一项,剩下的序列个数与A(n-1)的序列个数相等。例如:把A3的第一行序列的1去掉,剩下2和3,总共有四个序列。
所以,以f(n)来表示An的序列个数。则f(n) = n*[f(n-1) + 1],其中f(1) = 1。
我们以测试数据3 10为例,解释如何求解。
因为n=3,所以开始数组里1、2、3三个数。
可分成三组,每组5个序列。
因此第10个在第二组里。所以第一个是2,把2输出。原来的数组里删除2,变成1、3两个数。然后10 - (2 - 1) * 5 = 5,即它在第2组的第5个。
减去首个集合,5 - 1 = 4 ≠ 0,表明第10个序列不是第二组的首序列,而每一组只有首序列为一个元素的序列,所以2后面还有数字。
此时,剩下了数字1和3。可以组成四个序列。道理和A2={1,2}相同。此时,我觉得可以看成A2={1,3}。
剩下的序列又可以分成两组,每组2个序列。
4在第2组,剩下的数组中,第二个元素是3,所以输出3。再把数组里的3删除,剩下1。
然后4 - (2 - 1) * 2 = 2,即它是第2组的第2个。
减去首序列,2 - 1 = 1 ≠ 0,表示2后面还有数字。
按上面的方法继续下去,直到n = 0 或 后面为空集为止。
最后输出数组里的第1个元素,就得到2 3 1。
现在,贴上我的AC代码:
#include<iostream>
using namespace std;
#define N 21
__int64 a[N];
void init() //a[n]表示An的子序列个数
{
int i;
a[1]=1;
for(i=2;i<=20;i++)
a[i]=i*(a[i-1]+1); //An可以分成n组(1,2,...,n)
//每组除了第一个序列之外,n-1
//的子序列总数与每组剩下的序列
//总数相等,因为元素个数相等
//(虽然大小不相同)
}
int main()
{
int i,help,n;
int b[N];
__int64 m;
//__int64 temp;
init();
while(cin>>n>>m)
{
for(i=1;i<=n;i++)
b[i]=i;
while(m) //逐个元素输出
{
//temp=a[n]/n+1; //temp和help使用找到每次该输出
// help=m/temp+1; //的元素,加1是为了代码形式的
//cout<<b[help]; //统一
help=m/(a[n]/n)+(m%(a[n]/n)?1:0);
cout<<b[help];
m-=(help-1)*(a[n]/n)+1; //每输出一个元素,都应该缩减处理的数据范围
if( m )
cout<<" ";
for(i=help;i<n;i++) //缩减数据
b[i]=b[i+1];
n--; //缩减数据
}
cout<<endl;
}
return 0;
}
最后,大家一起看看问题所在吧。
请大家看看下面这段代码:
刚开始是用带注释的那一小段的,提交了四次,都WA了,相当纠结。
没办法,只有从网上找了,惭愧!发现呀,大家的逻辑以及算法都差不多。上面注释的部分是我自己想的,是用来确定第m个序列在An的第几行中。
未带注释的那部分也是同样的目的。但错误就在这里了。举个例子:A3={1,2,3},第4个序列、第5个序列和第6个序列所在行的求法。4/5=0,5/5=1,
6/5=1。所以就用temp=f(3)/3+1=6,行help=4/6+1=1,help=5/6+1=1,help=6/6+1=2。貌似解决了!但这只是总结,没有给出严谨的证明!事实证明,
这样做是错误的。
心得小结:
在解决问题时,往往我们都是唯用至上。没有太多关注底层的东西。一旦总结出来一点东西就会直接拿来用,不会再去追究,直到出错为止!
我在写C++时,很多时候都是一边迷惑一边敲代码。学过一点语言,就在凭着感觉写,一旦出现error,手足无措呀。比如C++中的唯一三目运
算,可能a=(b>c)?b:c,我们可以理解。那a=b?1:0呢?
很纠结于现状!
欢迎拍砖