hdu 1466 计算直线的交点数(经典dp)
http://acm.hdu.edu.cn/showproblem.php?pid=1466
平面上有n条直线,且无三线共点,问这些直线能有多少种不同交点数。
比如,如果n=2,则可能的交点数量为0(平行)或者1(不平行)。
分析:变化的根源在于有某些直线是平行的,所有不断产生新的结果(不是单纯的递推得到所有的结果)。对于a条直线,如果有r条直线是相互平行的,那么它相对于i-r直线集合的而言新增加的交点数目就是(i-r)*r。于是得到状态转移式子:if(dp[r][j]) dp[i][(i-r)*r+j]=1;
#include <iostream> #include <cstdio> using namespace std; int dp[25][200]; int main() { int n; for(int i=1;i<=20;i++){ dp[i][0]=1; //平行状态 } for(int i=1;i<=20;i++){ for(int r=0;r<i;r++){ int len=i*(i-1)/2; for(int j=0;(i-r)*r+j<=len;j++){ if(dp[r][j]) dp[i][(i-r)*r+j]=1; } } } while(cin>>n){ int length=n*(n-1)/2; for(int i=0;i<length;i++) if(dp[n][i])printf("%d ",i); printf("%d\n",length); } return 0; }
vijos P1098合唱队形 (DP 最长山峰序列长度)
【以前也遇到过类似的问题,最长上升子序列】
https://vijos.org/p/1098
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
分析:
最长山峰序列,即用求最长上升子序列的思路正着跑一次,反正跑一次,然后求出两种结果的长。
#include <iostream> #include <cstdio> using namespace std; int h[110],l[110],r[110],len[110]; int main() { int n; while(cin>>n){ for(int i=0;i<n;i++) scanf("%d",&h[i]); l[0]=1; for(int i=1;i<n;i++){ int maxm=0,dex=-1; for(int j=0;j<i;j++){ if(h[j]<h[i]&&maxm<l[j]){ maxm=l[j]; dex=j; } } if(dex>=0)l[i]=l[dex]+1; else l[i]=1; } //for(int i=0;i<n;i++) cout<<l[i]<<" "; cout<<endl; r[n-1]=1; for(int i=n-2;i>=0;i--){ int maxm=0,dex=-1; for(int j=n-1;j>i;j--){ if(h[j]<h[i]&&maxm<r[j]){ maxm=r[j]; dex=j; } } if(dex>=0)r[i]=r[dex]+1; else r[i]=1; } int ans=0; for(int i=0;i<n;i++){ len[i]=l[i]+r[i]-1; //cout<<len[i]<<" "; ans=ans>len[i]?ans:len[i]; } //cout<<endl; printf("%d\n",n-ans); } return 0; }
Vijos P1303导弹拦截(dp+greedy)
https://vijos.org/p/1303
相似问题:
hdu 1257 最少拦截系统(贪心)
相关博客:http://blog.csdn.net/thearcticocean/article/details/48031781
本题变化:“输出数据只有一行,该行包含两个数据,之间用半角逗号隔开。第一个数据表示这套系统最多能拦截的导弹数;第二个数据表示若要拦截所有导弹至少要再添加多少套这样的系统。”
最长不升子序列长度——第一问答案,使用DP:
第二问仍然是用贪心做的。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int h[25],tag[25],dp[25]; int main() { while(~scanf("%d",&h[0])){ int len=1; while(scanf(",%d",&h[len])) len++; memset(dp,0,sizeof(dp)); memset(tag,0,sizeof(tag)); dp[0]=1; int ans1=1; for(int i=1;i<len;i++){ int maxm=0,dex=-1; for(int j=0;j<i;j++){ if(h[i]<h[j]&&maxm<dp[j]){ maxm=dp[j]; dex=j; } } if(dex>=0)dp[i]=dp[dex]+1; else dp[i]=1; if(ans1<dp[i]) ans1=dp[i]; } int ans2=0; for(int i=len-1;i>=0;i--){ if(tag[i]==0){ ans2++; tag[i]=1; int temp=h[i]; for(int j=i-1;j>=0;j--){ if(h[j]>temp&&tag[j]==0){ tag[j]=1; temp=h[j]; } } } } printf("%d,%d\n",ans1,ans2-1); } return 0; <span style="font-size:14px;">}</span>
https://vijos.org/p/1122
栈是常用的一种数据结构,有n令元素在栈顶端一侧等待进栈,栈顶端另一侧是出栈序 列。你已经知道栈的操作有两·种:push和pop,前者是将一个元素进栈,后者是将栈顶元素弹出。现在要使用这两种操作,由一个操作序列可以得到一系列 的输出序列。请你编程求出对于给定的n,计算并输出由操作数序列1,2,…,n,经过一系列操作可能得到的输出序列总数。
分析:按照在某一个时刻的弹出情况,数字要么弹出,要么不弹出,所以有6种情况才对,4的话结果应该是6*4=24.(隔板)然而情况却不是这样,因为不存在3,1,2这样的结果,如果3先弹出,那么1,2全部先压进栈了才对,所以只有3,2,1。通过"压入"暂时看不出递推式,因为我们子问题的结果是弹出的结果数,所以也应该向弹出的情况发展思考,刚刚考虑的是第一个出来的数字,现在想想最后一个出来的数字,设它是i,那么比它小的数字1——i-1在它之前要弹出,比它大的数字也要先弹出,于是结果出来了:
#include <iostream> #include <cstdio> usingnamespacestd; int f[20]; int main() { f[0]=1; f[1]=1; for(int i=2;i<=15;i++){ for(int j=1;j<=i;j++){ //第i个元素最后出去 f[i]+=f[j-1]*f[i-j]; } } int n; while(cin>>n){ printf("%d\n",f[n]); } return0; }