原问题:
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;
}
痛定思痛,我决定再好好练练DP。
vijos P1098合唱队形 (DP 最长山峰序列长度)
【以前也遇到过类似的问题,最长上升子序列】
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)
相似问题:
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>
VIJOS P1122出栈序列统计 (catalan数 | DP)
栈是常用的一种数据结构,有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;
}
但是后来听别人说这是catalan数,又涨姿势了。。catalan数的另一种递推形式: