借助旧问题回顾动态规划

来源:互联网 发布:数控车图纸编程 编辑:程序博客网 时间:2024/06/08 11:21
一切要从一个问题说起,听说那是一道简单的动态规划题,于是我在半夜试着写写,谁知道到了第二天也没有写出来,看着45%的AC率,我想自己的动态规划已经忘得差不多了(呵呵,应该说没有学好)
原问题:

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; 

如果r条直线的交点情况存在【即全部平行或m个交点】,那么可以用它来讨论平行线相交。后面直线集合的多种平行情况可以由这样的状态转移一次次的算出来。
附上自制图片:

#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 最长山峰序列长度)

【以前也遇到过类似的问题,最长上升子序列】

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>

VIJOS P1122出栈序列统计 (catalan数 | DP)

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;}

但是后来听别人说这是catalan数,又涨姿势了。。catalan数的另一种递推形式:



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新老师教的不好怎么办 跟老公三观不合怎么办 突然有社保补扣怎么办 街头篮球篮板反应慢怎么办 换水了龙鱼顶缸怎么办 压着眼睛睡觉醒来模糊怎么办 天热眼睛有眼屎怎么办 眼睛里膜起来了怎么办 眼睛一边大一边小怎么办 眼白膜鼓起来了怎么办 主持问答环节没人提问怎么办 转学原学校不给怎么办 村长借东西不还怎么办 村长不上报建房申请怎么办 村长不上报建房手续申请怎么办 村长不给村民盖章怎么办? 找村干部办事难怎么办 洪洞县村长不给我盖章怎么办 睾丸穿刺取精只配到6个胚胎怎么办 孩子一只耳朵听不到声音怎么办 孕早期孕囊生长慢怎么办 试管2次不着床怎么办? pescm球员年龄大了怎么办 实况足球俱乐部经理球员老了怎么办 你不是我的菜怎么办 苹果平板电脑耳机有回音怎么办 obs直播有电流音怎么办 语音里网吧很吵怎么办 电脑k歌有延迟怎么办 想开个跆拳道馆怎么办营业执照? 壶嘴拐弯处漏水怎么办 裂纹茶壶嘴坏了怎么办 小孩刚上学怕她上火怎么办 在幼儿园小朋友不愿叠衣服怎么办 孩子在家听话幼儿园不听话怎么办 变魔术时观众说看过这个怎么办 孕妇8个月摔跤了怎么办 孩子吃了残奶怎么办 小米手机变卡了怎么办 主持时说错话了怎么办 小鲜肉老了不红怎么办