Hdu 1423 动态规划—最长公共上升子序列

可能写解题,对于ac的大家来说应该是一件比较高兴的事情吧。。但是这次我却是在省赛里面迷失了,原本这次省赛一道赤裸裸的LCIS,但是还是因为没有接触过,所以没能~~

过多的话不说了,还是好好写好解题吧。。

思路:

对于做dp的人而言,规划处最优子结构是解决一切题目的第一步,二此题的最优越子结构规划一下,

DP[i][j] 为序列1前i个元素和序列2前j个元素最长公共上升子序列多长。

那么这个时候初始值初始化为0的话,碰到序列1和序列2相等的情况只要依靠相等位置前的序列来得出状态即可,更新完整个dp数组。

代码如下:

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int N = 505;
 5 int num1[N],num2[N],dp[N][N];
 6 int main()
 7 {
 8     int t,n,m;
 9     scanf("%d",&t);
10     while(t--)
11     {
12          scanf("%d",&n);
13          for(int i=1;i<=n;i++)scanf("%d",&num1[i]);
14          scanf("%d",&m);
15          for(int j=1;j<=m;j++)scanf("%d",&num2[j]);
16          memset(dp,0,sizeof(dp));
17          int answer=0;
18          for(int i=1;i<=n;i++)
19          {
20              for(int j=1;j<=m;j++)
21              {
22                  if(num1[i]==num2[j])
23                  {
24                     for(int k=0;k<i;k++)
25                         if(num1[k]<num1[i])
26                         for(int l=0;l<j;l++)            
27                         dp[i][j]=max(dp[i][j],dp[k][l]+1);
28                  }
29                  answer=max(answer,dp[i][j]);
30              }        
31          }
32          printf("%d\n",answer);
33          if(t!=0)printf("\n");
34     }
35     return 0;    
36 }

虽然问题是解决了,但是复杂度还是过高,有O(n^4)。现在我们可以想一想,外层循环是代表的是序列1

我们就可以想到先是序列1前1项和序列2最长公共上升子序列长度,

再循环一次,就是序列1前2想和序列2最长公共子序列长度。

那么既然每次循环能够推动到后状态,那么现在我们改变一下原来的代码。

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int N = 505;
 5 int num1[N],num2[N],dp[N];
 6 int main()
 7 {
 8     int t,n,m;
 9     scanf("%d",&t);
10     while(t--)
11     {
12          scanf("%d",&n);
13          for(int i=1;i<=n;i++)scanf("%d",&num1[i]);
14          scanf("%d",&m);
15          for(int j=1;j<=m;j++)scanf("%d",&num2[j]);
16          memset(dp,0,sizeof(dp));
17          int answer=0;
18          int k;
19          for(int i=1;i<=n;i++)
20          {
21              for(int j=1;j<=m;j++)
22              {
23                  if(num1[i]==num2[j])
24                  {
25                         for(dp[j]=1,k=0;k<j;k++)          
26                         if(num2[k]<num2[j]&&dp[j]<dp[k]+1)
27                         dp[j]=dp[k]+1;
28                  }
29                  answer=max(answer,dp[j]);
30              }        
31          }
32          printf("%d\n",answer);
33          if(t!=0)printf("\n");
34     }
35     return 0;    
36 }

这个时候我们又可以想到,既然每次的后状态都依赖到了前状态,那么我是不是可以记录住前状态在DP[i][j]的数组当中然后,而不需要每次遇到相同的时候我们都从前状态当中去找那个形成的最长序列,答案就是用一个值来维护更新。改变代码为:

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int N = 505;
 5 int num1[N],num2[N],f[N][N];
 6 int main()
 7 {
 8     int t,n,m;
 9     scanf("%d",&t);
10     while(t--)
11     {
12          scanf("%d",&n);
13          for(int i=1;i<=n;i++)scanf("%d",&num1[i]);
14          scanf("%d",&m);
15          for(int j=1;j<=m;j++)scanf("%d",&num2[j]);
16          memset(f,0,sizeof(f));
17          int answer=0;
18          int ma;
19          for(int i=1;i<=n;i++)
20          {
21              ma=0;
22              for(int j=1;j<=m;j++)
23              {
24                  f[i][j]=f[i-1][j];
25                  if(num1[i]>num2[j]&&f[i-1][j]>ma)ma=f[i-1][j];
26                  if(num1[i]==num2[j])f[i][j]=ma+1;
27              }
28          }
29          for(int j=0;j<=m;j++)answer=max(answer,f[n][j]);
30          printf("%d\n",answer);
31          if(t!=0)printf("\n");
32     }
33     return 0;    
34 }

 

posted @ 2012-10-17 19:30  诺小J  阅读(1599)  评论(0编辑  收藏  举报