zoukankan      html  css  js  c++  java
  • USACO4.3 Buy Low, Buy Lower【简单dp·高精度】

    如果没有方案数的话,这道题水的不得了,裸的最长下降子序列。

    但是它有方案数,所以...

    一个是方案数的求法:

    设$f[i]$是以$a[i]$结尾的最长下降子序列的长度,可以$n^2$$dp$出答案 如果$a[j]>a[i],1<=j<=i-1$,可以更新$f[i]=max(f[i],f[j]+1)$,这个额老生常谈了

    设$s[i]$是以$a[i]$结尾的最长下降子序列的方案数,在更新$f[i]$的时候可以顺便更新$s[i]$:

    如果$f[i]==f[j]+1$,那么$s[i]=s[j]$

    如果$f[i]==f[j]$,那么$s[i]+=s[j]$

    在得到最长下降子序列的长度为$len$之后,把所有$f[i]==len$的$s[i]$全部加起来,就是总的方案数。

    但是,由于定义的是$s[i]$是以$a[i]$结尾的最长下降子序列的方案数,最长下降子序列的信息已经丢失,极有可能重复,比如:

    3 2 1 3 2 1

    后面那$3$个数的$s[]$都应该变为$0$
    否则的话$1$,$2$,$3$构成了数列$321$,$1$,$2$,$6$也构成了数列$321$,计算方案数就重复了。

    所以在两个位置$f[]$和$s[]$都相等的时候,就把那个位置置为$0$

    这么做的话,那么这种情况会不会出锅呢:

    6 5 4 6 5 3

    是不会的,因为把后一个$5$的方案数置为$0$之后,$3$还可以从前一个$5$转移过来,如果让$3$从两个地方都累加上了答案,那才会出锅。

    还有就是方案数会爆$long$ $long$,$_int128$也爆了,所以要用高精度。我直接用了封装成结构体的形式:

    https://www.cnblogs.com/lyttt/p/11805335.html

    (详见博客)

      1 //nice
      2 /*
      3 ID: Starry21
      4 LANG: C++
      5 TASK: buylow                
      6 */ 
      7 #include<cstdio>
      8 #include<algorithm>
      9 #include<vector>
     10 #include<cstring>
     11 #include<climits>
     12 using namespace std;
     13 #define N 5005
     14 #define ML 505//MaxLenth
     15 #define ll long long
     16 #define INF 0x3f3f3f3f
     17 struct BT//BigInt
     18 {
     19     int a[ML],len;
     20     BT()//初始化
     21     {
     22         memset(a,0,sizeof(a));
     23         len=1;
     24     }
     25     void Init()
     26     {
     27         a[0]=1;
     28     }
     29     BT operator + (const BT &A)const
     30     {
     31         BT B;
     32         B.len=max(len,A.len);
     33         for(int i=0;i<B.len;i++)
     34         {
     35             B.a[i]+=A.a[i]+a[i];
     36             if(B.a[i]>=10)
     37             {//进位 9+9=18 进位不会超过10 
     38                 B.a[i]-=10;
     39                 B.a[i+1]++;
     40             }
     41         }
     42         if(B.a[B.len])//进到了下一位
     43             B.len++; 
     44         return B;
     45     }
     46     void read()
     47     {
     48         char d[ML];
     49         scanf("%s",d);
     50         int l=strlen(d);
     51         for(int i=0;i<l;i++)
     52             a[i]=d[l-i-1]-'0';
     53         len=l;
     54     }
     55     void write()
     56     {
     57         for(int i=len-1;i>=0;i--)
     58             printf("%d",a[i]);
     59     }
     60 };
     61 ll rd()
     62 {
     63     ll f=1ll,x=0;char c=getchar();
     64     while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
     65     while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
     66     return f*x;
     67 }
     68 int n;
     69 ll a[N];
     70 int f[N];
     71 BT s[N];
     72 int main() 
     73 {
     74     //freopen("buylow.in","r",stdin);
     75     //freopen("buylow.out","w",stdout);
     76     scanf("%d",&n);
     77     for(int i=1;i<=n;i++)
     78         a[i]=rd();
     79     /*
     80     后面统计答案,是f[i]==max_long的s[i]全部加起来
     81     如果出现重复的 那个地方的s[]应该为0
     82     3 2 1 3 2 1
     83     后面那3个数的s[]都应该为0
     84     如果一来就赋了初值1 答案就会错
     85     */
     86     s[0].Init(),a[0]=LONG_MAX;
     87     for(int i=1;i<=n;i++)
     88     {
     89         for(int j=i-1;j>=0;j--)
     90             if(a[j]>a[i])
     91                 f[i]=max(f[i],f[j]+1);
     92         for(int j=i-1;j>=0;j--)
     93         {//记录方案数 
     94             if(a[j]>a[i]&&f[i]==f[j]+1) s[i]=s[i]+s[j];
     95             if(a[i]==a[j]&&f[i]==f[j]) break;
     96             /*
     97             防止重复
     98             3 2 1 3 2 1
     99             3 2 1是本质相同的序列
    100             是为了防止第6个数向第2个数转移的情况 
    101             */
    102         }
    103     }
    104     ll t1=0;BT t2;
    105     for(int i=1;i<=n;i++)
    106     {
    107         if(f[i]>t1)
    108             t1=f[i],t2=s[i];
    109         else if(f[i]==t1) t2=t2+s[i];
    110     } 
    111     printf("%lld ",t1);
    112     t2.write();
    113     puts("");
    114     return 0; 
    115 }
    Code
  • 相关阅读:
    解决硬盘分区无法挂载的问题
    Java EE学习记录(一)
    Netbeans 8.2启动参数含义及配置
    汉化CodeBlock
    linux解决无法打开资源管理器
    netbean下搭建mariadb数据库
    mariadb中执行数据库脚本的方法
    修复受损的linux引导
    修复无法启动的mariadb
    no update
  • 原文地址:https://www.cnblogs.com/lyttt/p/11809320.html
Copyright © 2011-2022 走看看