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代码:

 

View Code
 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编辑  收藏  举报

导航