poj1952
来源:互联网 发布:mysql 查看锁定的表 编辑:程序博客网 时间:2024/05/02 15:07
又是一道最长上升子序列的变形,让你求最长下降子序列的长度和个数,长度很简单,统计个数关键是不能重复,什么叫做重复呢,就是你找出来的所有情况,不可以存在2种序列完全一模一样,比如3 2 1 3 2 1这个问题答案 3 1,因为第一个 虽然有2个3 2 1出现,但是他们是重复的,只能算是一种。首先一个问题,假如现在不考虑重复,怎么求最长下降子序列的个数。对于这个问题,我首先想起来以前做过的一道关于最短路的条数的问题,有点类似,如果找到一个更优的状态,则赋值,如果找到了一个一样优的状态,则状态值加过去。先看下不考虑重复代码如下:
#include<stdio.h>int a[5001];int ans1,ans2;int sum[5001],len[5001];int main(){ int n; while(scanf("%d",&n)!=EOF) { int i,j; for(i=1;i<=n;i++) scanf("%d",a+i); ans1=ans2=0; for(i=1;i<=n;i++) { len[i]=1; sum[i]=1; for(j=i-1;j>=1;j--) { if(a[i]<a[j]) { if(len[i]==len[j]+1) sum[i]+=sum[j]; else if(len[i]<len[j]+1) { len[i]=len[j]+1; sum[i]=sum[j]; } } } if(ans1<len[i]) ans1=len[i]; } for(i=1;i<=n;i++) if(ans1==len[i]) ans2+=sum[i]; printf("%d %d\n",ans1,ans2); } return 0;}代码中len数组和sum数组分别表示的以第i个数为结尾的最长下降子序列的长度和个数,对于第i个数,如果我在前面找到了一个比它大的数a[j],那么也是分两种情况,第一,以j为结尾的最长下降子序列加上1能比当前以i为结尾的最长子序列更长,那么状态直接转移,相应的,要把个数赋值,因为当前找到了最优的,以i为结尾的最长下降子序列的个数一定等于以j为结尾的。第二,以j为结尾的最长下降子序列已经加上1和当前以i为结尾的最长子序列一样,那么有sum[i]+=sum[j],此时说明以i为结尾的最长下降子序列的个数一部分来源于以j为结尾的。最终的答案相信不难看懂。
但是上述代码不是本题的答案。
如何考虑重复呢?记得用O(nlogn)方法做最长上升子序列的时候,有一个重要的性质就是,假如我们找到了a[i]=a[j],i<j并且他们都可以作为a[k](k<i<j)状态转移选择(即a[k]<a[i]=a[j]),那么是选择a[i],而不是a[j],因为如果我们选了a[j],相当于假如i与j中间存在一个a[p]满足i,j的状态转移选择(即a[p]>a[i]=a[j]),那么选了j就相当于放弃了这个a[p]。所以在这里就产生了一个思想,如果存在相同的数,我只考虑前面的,ac代码如下:
#include<stdio.h>int a[5001];int ans1,ans2;int sum[5001],len[5001];int mark[5001];int main(){ int n; while(scanf("%d",&n)!=EOF) { int i,j; for(i=1;i<=n;i++) { scanf("%d",a+i); mark[i]=1; } ans1=ans2=0; for(i=1;i<=n;i++) { len[i]=1; sum[i]=1; for(j=i-1;j>=1;j--) { if(a[i]<a[j]&&mark[j]==1) { if(len[i]==len[j]+1) sum[i]+=sum[j]; else if(len[i]<len[j]+1) { len[i]=len[j]+1; sum[i]=sum[j]; } } else if(a[i]==a[j]) { if(len[i]==1) mark[i]=0; break; } } if(ans1<len[i]) ans1=len[i]; } for(i=1;i<=n;i++) if(ans1==len[i]) ans2+=sum[i]; printf("%d %d\n",ans1,ans2); } return 0;}就是在原来代码的基础上加上了这样的做法,处理第i个数时,如果我发现i的前面有个数a[j]==a[i],我就可以跳出j的循环了,因为j前面的数(a[1]到a[j-1])转移到i和转移到j是一样的,而j与i之间的数(a[j+1]到a[i-1])转移到i的状态我已经遍历过了,这里也说明了一点j的循环必须从i-1到1不能从小到大循环。如果j与i之间不存在大于a[i]的数,那么len[i]=1,此时a[i]这个数就是个“废数”,他的作用完全可以被a[j]取代,为了不让这个数影响它后面的数,我把它标记为0,并且每次往前找数的时候,只找标记为1的数。
0 0
- poj1952
- poj1952
- poj1952(经典dp题)
- POJ1952 BUY LOW, BUY LOWER
- POJ1952 BUY LOW, BUY LOWER
- poj1952 BUY LOW, BUY LOWER
- BUY LOW, BUY LOWER poj1952
- POJ1952 BUY LOW, BUY LOWER DP
- 【动态规划】poj1952 buy low,buy lower
- poj1952--BUY LOW, BUY LOWER题解
- POJ1952(最长下降子序列+去重)
- poj1952(BUY LOW,BUY LOWER)(dp记录方案个数)
- poj1952求最长递减子序列和最长子序列的种数
- POJ1952 BUY LOW, BUY LOWER(DP,最长下降子序列不重复计数)
- poj1952 BUY LOW, BUY LOWER(最长递减子序列及个数)
- 关于UILabel的一点使用心得
- NSOperationQueue与GCD的使用原则和场景
- Map的子类
- zclip 定位问题
- vs2010 watch 窗口调试
- poj1952
- 重定向和转发的区别
- java 超级for循环
- 树莓派RASPBIAN安装XBMC及最新中文电视
- android 测试题
- 在封装框架时遇到的一些action的问题
- ViewPager的使用(本例仅实现引导页滑动效果)
- 求一个字符串中某个子串的个数(C++)
- U-boot