BUY LOW, BUY LOWER POJ 1952
题意:给你每一天股票的价格,让你求出最长的递减子序列长度及该长度的子序列共有几个。这里要注意题目表明如果两个子序列的元素都相同,则这两个子序列只能算作一个。
题解:两次DP。首先求出最长的递减子序列的长度,状态转移方程为dp[i]=max(dp[j])+1;(0<=j<i);(下标从0开始)其中dp[i]表示以第i个数结尾的最长递减子序列的长度。这里在最后加一个dp[n]=-1,具体为什么在下一步讲述。
然后求每一种长度的递减子序列共有几个。状态转移方程:count[i]=sum(coun[j]);(其中a[i]<a[j]&&dp[j]+1==dp[i]);count[i]是以a[i]结尾的以dp[i]长度的递减子序列的个数。这里要注意排除子序列相同的情况!这里给出一个例子加以说明:
i 0 1 2 3 4 5 6
a[i] 5 8 4 4 3 2 -1
dp[i] 1 1 2 2 3 4 5
count[i] 1 1 2 2 2 2 2
这里在统计count[4]时,当加上count[3]后要忽略count[2]。
还有在数组最后加上a[n]=-1是保证count[n]一定是最终的答案,而不需要递推完count[]后再一一查找出最大的count[]中的最大值。
上面如果理解了dp[]和count[]的数组的意义之后此题的解法就很明了了。
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int N=5005; 5 int dp[N],a[N],count[N]; 6 int main() 7 { 8 bool mark; 9 int n,i,j,k,max; 10 while(scanf("%d",&n)!=EOF){ 11 for(i=0;i<n;i++) 12 scanf("%d",&a[i]); 13 a[n]=-1; 14 for(i=0;i<=n;i++){ 15 max=0; 16 for(j=i-1;j>=0;j--){ 17 if(a[i]<a[j]&&dp[j]>max)max=dp[j]; 18 } 19 dp[i]=max+1; 20 } 21 for(i=0;i<=n;i++){ 22 if(dp[i]==1){count[i]=1;continue;} 23 else count[i]=0; 24 for(j=i-1;j>=0;j--){ 25 if(a[j]>a[i]&&dp[j]==dp[i]-1){ 26 mark=true; 27 for(k=j+1;k<i;k++){ 28 if(a[k]==a[j]){ 29 mark=false; 30 break; 31 } 32 } 33 if(mark)count[i]+=count[j]; 34 } 35 } 36 } 37 printf("%d %d\n",dp[n]-1,count[n]); 38 } 39 return 0; 40 }
posted on 2012-09-20 22:25 Acmer_Roney 阅读(566) 评论(0) 编辑 收藏 举报