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

(详见博客)

USACO4.3 Buy Low, Buy Lower【简单dp·高精度】
  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

 

上一篇:最大最小值


下一篇:[mysql8]新坑哈 更改Mysql 表的大小转换设置lower_case_table_names=1