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 }
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com