HDU2062

     记录撰写以及调试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呢?

        很纠结于现状!

        

        欢迎拍砖

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值