HDOJ 2062 Subset sequence (dp)

来源:互联网 发布:什么是数据存储 编辑:程序博客网 时间:2024/06/05 14:47

HDOJ2062

这是我第一次在CSDN上写一些关于ACM的解题报告,自己只是刚刚学完C++的新手,大部分题目也只能借助解题报告完成。当然自己希望在学习的路上能收获一些东西,这就足够了。
HDOJ2062 大概DP(不懂DP,只是有一个类似转移方程,就想起了昨天看到DP关于状态方程的一些东西)。比如说求N=3时的情况,转化求N=2,转化为N=1。
状态转移方程和边界条件:
以f(n)来表示An的序列个数。
则f(n) = n*[f(n-1) + 1],其中f(1) = 1。

由于题目不是自己想出来的,感悟也不是很多。先记下别人的分析:

  首先,明确下解题思路。  显然,这题不能记录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>#include<ctype.h>using namespace std;int main(){    int i,n,t;  //t是所求子集位于分组后的第几组    __int64 c[21]={0};//字典分组后,每一组的个数    __int64 m; //要求的集合是第几个    int s[21]; //字典序分组后,每组初始化的首元素    for(i=1;i<21;i++)        c[i]=(i-1)*c[i-1]+1;    while(scanf("%d%I64d",&n,&m)!=EOF){ //n是A中有多少个数,m是排序后的位置        for(i=0;i<20;i++)            s[i]=i; //每循环一次就要重新归位首元素,因为每次循环删除了该元素        while(n>0&&m>0){  //边界条件            t=m/c[n]+(m%c[n]>0?1:0);             if(t>0){  //t是分组后m所在第几组            cout<<s[t]; //输出首元素            for(i=t;i<=n;i++)                s[i]=s[i+1]; //删除所在组数的首元素            m-=((t-1)*c[n]+1); //求出m所在该组的第几个            putchar(m==0?'\n':' ');                         }        n--;  //依次递减        }    }}
原创粉丝点击