zoukankan      html  css  js  c++  java
  • 值得一做》关于数学与递推 BZOJ1002 (BZOJ第一页计划)(normal+)

      什么都不说先甩题目

    Description

      轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的。一个N轮状基由圆环上N个不同的基原子
    和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道。如下图所示

      N轮状病毒的产生规律是在一个N轮状基中删去若干条边,使得各原子之间有唯一的信息通道,例如共有16个不
    同的3轮状病毒,如下图所示

      现给定n(N<=100),编程计算有多少个不同的n轮状病毒

     

    Input

      第一行有1个正整数n

    Output

      计算出的不同的n轮状病毒数输出

    Sample Input

    3

    Sample Output

    16

      这道题其实思路应该是生成树计数问题,但是很明显的,本题给出的数据范围对于一般使用的Matrix-Tree定理(Kirchhoff矩阵-树定理)来说数据范围有点大,所以在这里Matrix-Tree只作为暴力算法,正解得出的方法是用奇怪的递推式。

      首先来讲讲Matrix-tree,具体方法是这样:

      一,首先得出一个点的度的矩阵A,定义为:对于当前矩阵A[i][j]来说,只有在当前i=j的情况下,A[i][j]为第i个点的度数,其他点为0;

      二,得出一个关于边的矩阵B,定义为:对于当前的矩阵B[i][j]来说,当i与j相连的情况下,B[i][j]为1,其他点为0;

      最后得出一个最终矩阵C,对于C的定义为:C[i][j]=A[i][j] - B[i][j];

      我们在最后的计算前要对矩阵C做一件玄学的事情:以一个对角线数字作为关键点,消掉该关键带点所在横行于纵行所有点(包括自己),把剩下的点再拼成矩阵C(事关玄学);

      接下来就很简单了,把新矩阵C当做一组线性方程,然后用高斯消元对C来求解(只需要把由对角线切开的任意一个数字三角上的数全消为0即可),然后把对角线上的数字全部相乘,得出解;

      贴出以这种方法写的n<20的暴力

     1 #include<stdio.h>
     2 double mp[110][110];
     3 int n;
     4 int main()
     5 {
     6     scanf("%d",&n);
     7     if(n==1){printf("1");return 0;}//来组特判 
     8     if(n==2){printf("5");return 0;}
     9     mp[1][1]=3,mp[1][n]=-1,mp[1][2]=-1,mp[n][n]=3,mp[n][1]=-1,mp[n][n-1]=-1;
    10     for(int i=2;i<n;i++)mp[i][i-1]=mp[i][i+1]=-1,mp[i][i]=3;//以下是建立矩阵的过程 
    11     for(int i=2;i<=n;++i)//对于这道题,把中间的点看做要消掉的点,建矩阵方法因人而异 
    12     {
    13         for(int j=1;j<i;++j)
    14         {
    15             double shit=(-mp[i][j])/mp[j][j];
    16             for(int k=j;k<=n;++k)
    17             mp[i][k]+=mp[j][k]*shit;
    18         }
    19     }
    20     double ans=1;
    21     for(int i=1;i<=n;i++)
    22         ans*=mp[i][i];
    23     printf("%d",(int)(ans+0.5));//四舍五入 
    24     return 0;
    25 }
    Matrix-tree

      但是这样并不能A,所以我们要想一种更优的解,

      根据对打表数据的判断,可以得出g[i]=3*g[i-1]-g[i-2]+2的神奇结论;

      这个公式好像要依靠对于数论的理解来分析(对于新加入的点对于全图的关系),得出递推式。

      贴出正解代码

     1 #include<stdio.h>
     2 struct shit{
     3     int a[1000],len;
     4 }g[110];
     5 int n;
     6 shit c(shit x,int y)
     7 {
     8     for(int i=1;i<=x.len;i++)x.a[i]*=y;
     9     for(int i=1;i<=x.len;i++)
    10     {
    11         x.a[i+1]+=x.a[i]/10;
    12         x.a[i]%=10;
    13     }
    14     if(x.a[x.len+1])x.len++;
    15     return x;
    16 }
    17 shit j(shit x,shit y)
    18 {
    19     x.a[1]+=2;
    20     int s=x.len;
    21     shit z;
    22     for(int i=1;i<=x.len;i++)
    23     {
    24         if(x.a[i]<y.a[i])x.a[i+1]--,x.a[i]+=10;
    25         x.a[i]=x.a[i]-y.a[i];
    26     }
    27     while(x.a[x.len]==0)x.len--;
    28     return x;
    29 }
    30 int main()
    31 {
    32     scanf("%d",&n);
    33     g[1].a[1]=1,g[1].len=1;
    34     g[2].a[1]=5,g[2].len=1;
    35     for(int i=3;i<=n;++i)
    36         g[i]=j(c(g[i-1],3),g[i-2]);
    37     for(int i=g[n].len;i>=1;i--)printf("%d",g[n].a[i]);
    38     return 0;
    39     
    40 } 
    View Code

    另外还有一种思路提供参考

    【规律】这里是1--15的打表情况:1 5 16 45 121 320 841 2205 5776 15125 39601 103680 271441 710645 1860496。很快先发现一个规律:第1、3、5、7位是平方数,2、4、6、8位除以5后也是平方数。

        然后再整理:1*1 5*1*1 4*4 5*3*3 11*11 5*8*8 29*29 5*21*21 76*76 5*55*55 199*199 5*144*144 521*521。看着奇数位1,3,8,21,55。。。。。。灵光一现:这不是斐波那契的一半吗:1,2,3,5,8,13,21,24,55.。。。。。另外一个也能表示成类似的相加的数列:

    奇数位:1 3 4 7 11 18 29 76

    偶数位:1 2 3 5 8 13 21 34 55

    主要结论来自于:http://blog.csdn.net/jiangshibiao/article/details/22645557

    然后贴出以这种方式A的神犇的代码

     1 #include<cstdio>
     2 #include<cstring>
     3 using namespace std;
     4 const int size=51;                 //这里的位数卡着n=100的情况,为了加速。
     5 struct arr{int num,p[size];}a,b,c;
     6 int i,n,j;
     7 arr add(arr a,arr b)
     8 {
     9   arr c;memset(c.p,0,sizeof(c.p));
    10   c.num=a.num>b.num?a.num:b.num;
    11   for (int i=1;i<=c.num;i++)
    12     c.p[i]=a.p[i]+b.p[i];
    13   for (int i=1;i<=c.num;i++)
    14     c.p[i+1]+=c.p[i]/10,c.p[i]%=10;
    15   if (c.p[c.num+1]) c.num++;
    16   return c;
    17 }
    18 arr chen(arr a,arr b)
    19 {
    20   arr c;memset(c.p,0,sizeof(c.p));
    21   for (int i=1;i<=a.num;i++)
    22     for (int j=1;j<=b.num;j++)
    23       c.p[i+j-1]+=a.p[i]*b.p[j];
    24   c.num=a.num+b.num-1;
    25   for (int i=1;i<=c.num;i++)
    26     c.p[i+1]+=c.p[i]/10,c.p[i]%=10;
    27   while (c.p[c.num+1])
    28   {
    29     c.num++;c.p[c.num+1]+=c.p[c.num]/10;c.p[c.num]%=10;
    30   }
    31   return c;
    32 }
    33 int main()
    34 {
    35   scanf("%d",&n);
    36   switch (n) 
    37   {
    38     case 0:{printf("0");return 0;}
    39     case 1:{printf("1");return 0;}
    40     case 2:{printf("5");return 0;}
    41   }
    42   if (n%2==0)
    43   {
    44     a.p[1]=1;b.p[1]=2;a.num=1;b.num=1;
    45     for (i=4;i<=n;i++)
    46     {
    47       c=add(a,b);
    48       a.num=b.num;for (j=1;j<=a.num;j++) a.p[j]=b.p[j];
    49       b.num=c.num;for (j=1;j<=b.num;j++) b.p[j]=c.p[j];
    50     }
    51     c=chen(c,c);
    52     memset(a.p,0,sizeof(a.p));a.p[1]=5;a.num=1;c=chen(c,a);
    53     for (i=c.num;i>0;i--)
    54       printf("%d",c.p[i]);
    55   }
    56   else
    57   {
    58     a.p[1]=1;b.p[1]=3;a.num=1;b.num=1;
    59     for (i=3;i<=n;i++)
    60     {
    61       c=add(a,b);
    62       a.num=b.num;for (j=1;j<=a.num;j++) a.p[j]=b.p[j];
    63       b.num=c.num;for (j=1;j<=b.num;j++) b.p[j]=c.p[j];
    64     }
    65     c=chen(c,c);
    66     for (i=c.num;i>0;i--)
    67       printf("%d",c.p[i]);
    68   }
    69   return 0;
    70 }
    View Code
  • 相关阅读:
    公用表表达式(CTE)的递归调用
    c# 如何让tooltip显示文字换行
    实战 SQL Server 2008 数据库误删除数据的恢复
    SQL SERVER数据库中 是否可以对视图进行修改删除
    asp.net中实现文件批量上传
    sql server 2008学习2 文件和文件组
    sql server 2008学习3 表组织和索引组织
    sql server 2008学习4 设计索引的建议
    sql server 2008学习10 存储过程
    .net 调用 sql server 自定义函数,并输出返回值
  • 原文地址:https://www.cnblogs.com/PencilWang/p/5907796.html
Copyright © 2011-2022 走看看