zoukankan      html  css  js  c++  java
  • SCOI2009游戏

    1025: [SCOI2009]游戏

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 1065  Solved: 673
    [Submit][Status]

    Description

    windy学会了一种游戏。对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。最开始windy把数字按顺序1,2,3,……,N写一排在纸上。然后再在这一排下面写上它们对应的数字。然后又在新的一排下面写上它们对应的数字。如此反复,直到序列再次变为1,2,3,……,N。 如: 1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下 1 2 3 4 5 6 2 3 1 5 4 6 3 1 2 4 5 6 1 2 3 5 4 6 2 3 1 4 5 6 3 1 2 5 4 6 1 2 3 4 5 6 这时,我们就有若干排1到N的排列,上例中有7排。现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。

    Input

    包含一个整数,N。

    Output

    包含一个整数,可能的排数。

    Sample Input

    【输入样例一】
    3


    【输入样例二】
    10

    Sample Output

    【输出样例一】
    3

    【输出样例二】
    16


    【数据规模和约定】
    30%的数据,满足 1 <= N <= 10 。
    100%的数据,满足 1 <= N <= 1000 。

    HINT

    题解:

    题目所描述的对应变换关系实质就是一个置换
    而任意一个置换都可以写成若干轮换的乘积。如题目所述1->2  2->3  3->1  4->5  5->4  6->6,可以写成:
    (1 2 3)(4 5)(6) 
    排数实际上就是若干循环节长度的最小公倍数。这里就是:3*2*1
    因此所求即是:加和恰好为N的若干正整数的最小公倍数的可能数。
    首先,由于1不影响最小公倍数(若干个整数里面就有可能有1),所以,问题转化为:
    加和小于等于N的若干正整数的最小公倍数的可能数。
    对于a1+a2+a3+a4+...+ai<=N里,任意一个正整数ai都可以表示为若干个素数的乘积  ps:我想到这儿就不会了……
    对于这若干个素数:乘积>=和
    所以问题就转化为:加和小于等于N的若干只含一个质因数的正整数的最小公倍数的可能数。
    然后先弄一个素数表。
    用dp[i,j]表示前i个素数(和它的k次幂)之和小于等于j时有多少个最小公倍数
    则答案为dp[num,n](num为小于等于N的素数个数)
    方程为
    dp[i,j]:=sum(dp[i-1,j-sushu[i]^k])注意k>=0且sushu[i]^k<=j
    这个公式不完全对,当k=0的时候要特殊处理!这个时候加的值不是dp[i-1,j-1]而是dp[i-1,j]。

    实际上,问题到了后面,也就是到了将问题转化为加和小于等于N的若干只含一个质因数的正整数的最小公倍数的可能数。

    这俨然已经成为了一个背包问题求构成种数的问题。

    有一个容量为n个背包,有很多物体,它们的体积分别问:

    1,2,2*2,2*2*2,……3,3*3,3*3*3……

    并且是01背包问题(除了1可以无限用),你想放两个2*2,和放一个2*2再放4个1的最小公倍数不是一样的吗?枚举到一个上限即可

    下面我将给出三份代码:

    一、vijosp1071新年趣事之打牌(这是我第一次接触背包问题求构成种数的问题,所以文件里有记录)

    题目附在另一篇文章里

    代码如下:

     1 var n,i,j,tot,ans:longint;
     2     a,f,g:array[0..10000] of longint;
     3     p:array[0..10000]of boolean;
     4 begin
     5 readln(tot);
     6 readln(n);f[0]:=1;
     7 for i:=1 to n do readln(a[i]);
     8 for i:=1 to n do
     9  for j:=tot downto a[i] do
    10   begin
    11   if (f[j-a[i]]>0) and (f[j]=0) then g[j]:=i;
    12   f[j]:=f[j]+f[j-a[i]];
    13   end;
    14 i:=tot;
    15 if f[tot]>1 then begin writeln('-1');halt;end;
    16 if f[tot]=0 then begin writeln('0');halt;end;
    17 while (i>0) and (g[i]<>0) do
    18  begin
    19  p[g[i]]:=true;
    20  i:=i-a[g[i]];
    21  end;
    22 for i:=1 to n do if not p[i] then write(i,' ');
    23 end.                  
    View Code

    二、本题二维做法,比较费空间

    代码:

     1 var i,j,k,n,tot,tmp:longint;
     2     check:array[0..1500] of boolean;
     3     p:array[0..1500] of longint;
     4     f:array[0..1500,0..1500] of int64;
     5 procedure getprimes;
     6  var i,j,k:longint;
     7  begin
     8  tot:=0;
     9  fillchar(check,sizeof(check),true);
    10  for i:=2 to n do
    11   begin
    12   if check[i] then begin inc(tot);p[tot]:=i;end;
    13   for j:=1 to tot do
    14    begin
    15    k:=i*p[j];
    16    if k>n then break;
    17    check[k]:=false;
    18    if i mod p[j]=0 then break;
    19    end;
    20   end;
    21  end;
    22 procedure main;
    23  begin
    24  readln(n);
    25  getprimes;
    26  for i:=0 to n do f[0,i]:=1;
    27  for i:=0 to tot do f[i,0]:=1;
    28  for i:=1 to tot do
    29   begin
    30   for j:=1 to n do
    31    begin
    32    inc(f[i,j],f[i-1,j]);
    33    k:=p[i];
    34    while k<=j do
    35     begin
    36     inc(f[i,j],f[i-1,j-k]);
    37     k:=k*p[i];
    38     end;
    39    end;
    40   end;
    41  writeln(f[tot,n]);
    42  end;
    43 begin
    44  main;
    45 end.       
    View Code

    三、本题一维做法

    代码:

     1 var i,j,k,n,tot,tmp:longint;
     2     check:array[0..1500] of boolean;
     3     p:array[0..1500] of longint;
     4     f:array[0..1500] of int64;
     5 procedure getprimes;
     6  var i,j,k:longint;
     7  begin
     8  tot:=0;
     9  fillchar(check,sizeof(check),true);
    10  for i:=2 to n do
    11   begin
    12   if check[i] then begin inc(tot);p[tot]:=i;end;
    13   for j:=1 to tot do
    14    begin
    15    k:=i*p[j];
    16    if k>n then break;
    17    check[k]:=false;
    18    if i mod p[j]=0 then break;
    19    end;
    20   end;
    21  end;
    22 procedure main;
    23  begin
    24  readln(n);
    25  getprimes;
    26  for i:=0 to n do f[i]:=1;
    27  for i:=1 to tot do
    28   for j:=n downto 1 do
    29    begin
    30    k:=p[i];
    31    while k<=j do
    32     begin
    33     inc(f[j],f[j-k]);
    34     k:=k*p[i];
    35     end;
    36    end;
    37  writeln(f[n]);
    38  end;
    39 begin
    40  main;
    41 end.     
    View Code

    需要注意一点:将二维转化为一维后,内循环变成了倒序,而二维的话是不用考虑这个的(自己想想为什么)

  • 相关阅读:
    我的WCF之旅(1):创建一个简单的WCF程序
    网页设计中颜色的搭配
    CSS HACK:全面兼容IE6/IE7/IE8/FF的CSS HACK
    UVa 1326 Jurassic Remains
    UVa 10340 All in All
    UVa 673 Parentheses Balance
    UVa 442 Matrix Chain Multiplication
    UVa 10970 Big Chocolate
    UVa 679 Dropping Balls
    UVa 133 The Dole Queue
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/3812779.html
Copyright © 2011-2022 走看看