poj1952 BUY LOW, BUY LOWER(最长递减子序列及个数)
2017-09-06 17:51
399 查看
原题: http://poj.org/problem?id=1952
就是求最长递减子序列dp ,以及求不同的子序列的个数r ...思路和代码较长,请大牛见怪莫怪...
想法:我们需要在求最长递减子序列的过程中加一些额外的步骤,顺便把不同的子序列数目也算出来....明白下面两点其实就差不多了...详细过程看不看都行..
首先要明白:1. 对于相同的两个数ai,aj,ai在aj之前,一定有最长子序列dp[j]>=dp[i];且当最长子序列长度dp[i]==dp[j]时, a2的不同子序列数目>=a1的不同子序列数目(后面简称情况数) ; 2.对于两个相同的数ai,aj,ai在aj前面,如果dp[i]==dp[j],且集合rs.size()>=2(即这个长度tmp前面已经有两个数字满足了),说明ai,aj之间一定隔了一些比ai,ai还要小的数字,所以情况数目kind[i]一定等于kind[j]。如果rs.size()==1,那么根据①,对于子序列长度相等,数字相同,后面出现的比前面出现的情况数多..直接r=kind[i]
下面是详细过程
所以我们需要一个额外的数组kind[]去记录各个位置的情况数,最长子序列长度为max,用一个集合set<int>rs,记录子序列长度为max的 那些数字,后面就知道为什么了
首先是求最长递减子序列的过程
外循环for(int i=1;i<n;i++)
int tmp=1;
......
内循环 for(int j=i-1;j>=0;j--)
if(tmp<dp[j])...
.......
我们需要在 if(tmp<dp[j]),多加一点修改:,我们需要求出每一个位置i的情况数目,先借用一个变量ans记录位置 i 的情况数
1.对于子序列长度相同tmp==dp[j]的情况: 因为第二层循环我们是从后往前遍历,根据定理①对应相同的数字,后面的出现的情况数目一定>=前面的情况数,所以如果这个数出现过,则不用理睬,如果没出现过就 把相应的情况数目kind[j]加进来。判断有没有出现过,我们可以借用set。
2.对于子序列长度不同tmp<dp[j]的情况: 直接选择较长的那一个,情况数目ans 等于那个位置的情况数目
外循环for(int i=1;i<n;i++)
int tmp=1;
int ans=0;
set<int>s;//记录a[j]是否出现过
......
for(int j=i-1;j>=0;j--)
if(tmp==dp[j]){
if(s.count(a[j])==0) ...
else ...
}
else if(tmp<dp[j]) ans=kind[j]
求得tmp 和 kind[i]后,我们自然需要去跟max比较,求最长嘛
我门要明白的第二点 2.对于两个相同的数ai,aj,ai在aj前面,如果dp[i]==dp[j],且集合rs.size()>=2(即这个长度tmp前面已经有两个数字满足了),说明ai,aj之间一定隔了一些比ai,ai还要小的数字,所以情况数目kind[i]一定等于kind[j]。如果rs.size()==1,那么根据①,对于子序列长度相等,数字相同,后面出现的比前面出现的情况数多..直接r=kind[i]
if(tmp>max) ....直接更新 max=tmp ; r=kind[i] ; rs.insert(a[i])
else if(tmp==max) 判断 if (rs.count(a[i])==0 ) ... else if (rs.size()==1) ...
就是求最长递减子序列dp ,以及求不同的子序列的个数r ...思路和代码较长,请大牛见怪莫怪...
想法:我们需要在求最长递减子序列的过程中加一些额外的步骤,顺便把不同的子序列数目也算出来....明白下面两点其实就差不多了...详细过程看不看都行..
首先要明白:1. 对于相同的两个数ai,aj,ai在aj之前,一定有最长子序列dp[j]>=dp[i];且当最长子序列长度dp[i]==dp[j]时, a2的不同子序列数目>=a1的不同子序列数目(后面简称情况数) ; 2.对于两个相同的数ai,aj,ai在aj前面,如果dp[i]==dp[j],且集合rs.size()>=2(即这个长度tmp前面已经有两个数字满足了),说明ai,aj之间一定隔了一些比ai,ai还要小的数字,所以情况数目kind[i]一定等于kind[j]。如果rs.size()==1,那么根据①,对于子序列长度相等,数字相同,后面出现的比前面出现的情况数多..直接r=kind[i]
下面是详细过程
所以我们需要一个额外的数组kind[]去记录各个位置的情况数,最长子序列长度为max,用一个集合set<int>rs,记录子序列长度为max的 那些数字,后面就知道为什么了
首先是求最长递减子序列的过程
外循环for(int i=1;i<n;i++)
int tmp=1;
......
内循环 for(int j=i-1;j>=0;j--)
if(tmp<dp[j])...
.......
我们需要在 if(tmp<dp[j]),多加一点修改:,我们需要求出每一个位置i的情况数目,先借用一个变量ans记录位置 i 的情况数
1.对于子序列长度相同tmp==dp[j]的情况: 因为第二层循环我们是从后往前遍历,根据定理①对应相同的数字,后面的出现的情况数目一定>=前面的情况数,所以如果这个数出现过,则不用理睬,如果没出现过就 把相应的情况数目kind[j]加进来。判断有没有出现过,我们可以借用set。
2.对于子序列长度不同tmp<dp[j]的情况: 直接选择较长的那一个,情况数目ans 等于那个位置的情况数目
外循环for(int i=1;i<n;i++)
int tmp=1;
int ans=0;
set<int>s;//记录a[j]是否出现过
......
for(int j=i-1;j>=0;j--)
if(tmp==dp[j]){
if(s.count(a[j])==0) ...
else ...
}
else if(tmp<dp[j]) ans=kind[j]
求得tmp 和 kind[i]后,我们自然需要去跟max比较,求最长嘛
我门要明白的第二点 2.对于两个相同的数ai,aj,ai在aj前面,如果dp[i]==dp[j],且集合rs.size()>=2(即这个长度tmp前面已经有两个数字满足了),说明ai,aj之间一定隔了一些比ai,ai还要小的数字,所以情况数目kind[i]一定等于kind[j]。如果rs.size()==1,那么根据①,对于子序列长度相等,数字相同,后面出现的比前面出现的情况数多..直接r=kind[i]
if(tmp>max) ....直接更新 max=tmp ; r=kind[i] ; rs.insert(a[i])
else if(tmp==max) 判断 if (rs.count(a[i])==0 ) ... else if (rs.size()==1) ...
#include<cstdio> #include<set> using namespace std; int main() { int n; const int size=5100; while(~scanf("%d",&n)) { int a[size]; for(int i=0;i<n;i++) { scanf("%d",&a[i]); } //最长子序列为max int max=1;//结果长度 int r=1;//不同的子序列数目 set<int>rs; //存放最长子序列长度为max的那些数字 rs.insert(a[0]); int kind[size]={1};//记录不同位置的不同子序列数目 int dp[size]={1};//到i的最长序列长度 for(int i=1;i<n;i++) { int tmp=0; int ans=1; set<int>s2; for(int j=i-1;j>=0;j--)//从后向前面遍历 { if(a[i]<a[j]) { if(tmp<dp[j])//如果小于,直接更新 { s2.clear(); s2.insert(a[j]); tmp=dp[j]; ans=kind[j]; }else if(tmp==dp[j]) { if(s2.count(a[j])==0) //是否出现过 { s2.insert(a[j]); ans=ans+kind[j]; } } } } dp[i]=tmp+1;//记录最长子序列的长度 kind[i]=ans;//记录位置i的情况数目 if(max<dp[i])//如果大于max,直接更新 { max=dp[i]; //更新最长 r=kind[i]; rs.clear();//清空 rs.insert(a[i]);//插入 }else if(max==dp[i])//如果等于 { //先判是否出现过 if(rs.count(a[i])==0)//没有出现过直接加到r上面 { rs.insert(a[i]); r=r+kind[i]; }else if(rs.size()==1)//根据①如果出现过了,但是集合只有一个元素,那么根据定理①,直接更新r即可。根据②,rs.size()>=2不用理睬 { r=kind[i]; } } } printf("%d %d\n",max,r); } return 0; } //ac
相关文章推荐
- BUY LOW, BUY LOWER----POJ_1952----最长递减子序列
- poj 1952 BUY LOW, BUY LOWER 最长下降子序列计数
- POJ 1952 BUY LOW, BUY LOWER(最长递减序列,去重, 动态规划)
- poj 1952 BUY LOW, BUY LOWER 最长下降子序列+统计不重复方案数
- POJ 1952 BUY LOW,BUY LOWER 最长递减子序列 动态规划
- POJ 1552 BUY LOW, BUY LOWER(最长单调递减子序列求方案数)
- POJ - 1952 BUY LOW, BUY LOWER(最长下降子序列,计数)
- poj 1952 BUY LOW, BUY LOWER(求最长下降子序列个数)
- POJ 1952 LITTLE SHOP OF FLOWERS(DP动归,最长递减子序列)
- poj 1952 BUY LOW, BUY LOWER[最长单调子序列变形]
- 【最长下降子序列】北大 poj 1952 BUY LOW, BUY LOWER
- poj 1952 最长递减子序列+求个数
- poj 1952 BUY LOW, BUY LOWER【解法一】
- POJ1952 BUY LOW, BUY LOWER
- POJ 1952(最长不下降子序列的个数)
- POJ 1952 BUY LOW, BUY LOWER
- POJ 1952 BUY LOW, BUY LOWER
- POJ 1952 BUY LOW, BUY LOWER
- poj 1952 BUY LOW, BUY LOWER
- POJ 1952 BUY LOW, BUY LOWER 笔记