20200411(动态规划)
1、最大跳跃( jump.cpp ) hdu 1087
这个题目其实就是一个最长不下降子序列的题目,但是不同的是需要知道最大的序列和,比较基础
1、最大跳跃( jump.cpp ) hdu 1087 【问题描述】 最近有一个跳跃游戏十分受欢迎。 这个游戏可以由两个或两个以上的玩家玩。它由一个棋盘和一些棋子组成,所有棋子都用正整数或“start”或“end”标记。玩家从起点开始,最后必须跳到终点。在跳跃的过程中,玩家将访问路径中的棋子,但是每个人都必须从一个棋子跳到另一个绝对更大的棋子(你可以假设起点是最小值,终点是最大值) 。所有玩家都不能后退,一次跳跃可以从一个棋子跳到下一个,也可以跨越多个棋子,甚至你可以直接从起点跳到终点(在这种情况下,你当然会得零分)。如果只有一个玩家能够根据他的跳跃解决方案获得更高的分数时,他才是赢家。请注意,您的分数来自您跳跃路径中棋子的总价值。你的任务是根据给定的棋子列表输出最大值。 【输入说明】 输入包含多个测试用例。每一个测试用例都被描述成如下一行: n value1 value2 ……valuen 保证n不超过1000,并且所有的值I都在32-int的范围内。从0开始的测试用例终止输入,并且这个测试用例不被处理。 【输出说明】 对于每种情况,根据规则打印最大值,一行一种情况。 【输入样例1】 3 1 3 2 4 1 2 3 4 4 3 3 2 1 0 【输出样例1】 4 10 3
#include <bits/stdc++.h> using namespace std; const int N=1005,inf=0x3f3f3f; int n,a[N],f[N],ans; int main() { while(cin>>n&&n){ memset(f,0,sizeof(f)); memset(a,0,sizeof(a)); for(int i=1;i<=n;i++){ cin>>a[i];f[i]=a[i]; } ans=f[1]; for(int i=2;i<=n;i++) { for(int j=1;j<i;j++) if(a[j]<a[i]) f[i]=max(f[i],f[j]+a[i]); ans=max(ans,f[i]); } cout<<ans<<endl; } return 0; }
2、龟兔赛跑
这个题目就是一个动态规划,就是自己去定义一个状态,d[i]表示到第i个站点充电时最少花费的时间,一些小的限制条件应该是比较好想到的,注意要用double 类型来进行比较
2、 龟兔赛跑 hdu 2059 【问题描述】 据说在很久很久以前,可怜的兔子经历了人生中最大的打击——赛跑输给乌龟后,心中郁闷,发誓要报仇雪恨,于是卧薪尝胆潜心修炼,终于练成了绝技,能够毫不休息得以恒定的速度(VR m/s)一直跑。兔子一直想找机会好好教训一下乌龟,以雪前耻。兔子向乌龟发起挑战。虽然乌龟深知获胜希望不大,不过迫于舆论压力,只能接受挑战。 比赛是设在一条笔直的道路上,长度为L米,规则很简单,谁先到达终点谁就算获胜。无奈乌龟自从上次获胜以后,成了名龟,被一些八卦杂志称为“动物界的刘翔”,广告不断,手头也有了不少积蓄。为了能够再赢兔子,乌龟不惜花下血本买了最先进的武器——“"小飞鸽"牌电动车。这辆车在有电的情况下能够以VT1 m/s的速度“飞驰”,可惜电池容量有限,每次充满电最多只能行驶C米的距离,以后就只能用脚来蹬了,乌龟用脚蹬时的速度为VT2 m/s。更过分的是,乌龟竟然在跑道上修建了很多很多(N个)的供电站,供自己给电动车充电。其中,每次充电需要花费T秒钟的时间。当然,乌龟经过一个充电站的时候可以选择去或不去充电。 比赛马上开始了,兔子和带着充满电的电动车的乌龟并列站在起跑线上。你的任务就是写个程序,判断乌龟用最佳的方案进军时,能不能赢了一直以恒定速度奔跑的兔子。 【输入说明】 本题目包含多组测试,请处理到文件结束。每个测试包括四行: 第一行是一个整数L代表跑道的总长度 第二行包含三个整数N,C,T,分别表示充电站的个数,电动车冲满电以后能行驶的距离以及每次充电所需要的时间 第三行也是三个整数VR,VT1,VT2,分别表示兔子跑步的速度,乌龟开电动车的速度,乌龟脚蹬电动车的速度 第四行包含了N(N<=100)个整数p1,p2...pn,分别表示各个充电站离跑道起点的距离,其中0<p1<p2<...<pn<L 其中每个数都在32位整型范围之内。 【输出样例】 当乌龟有可能赢的时候输出一行 “What a pity rabbit!"。否则输出一行"Good job,rabbit!"; 题目数据保证不会出现乌龟和兔子同时到达的情况。 【输入样例】 100 3 20 5 5 8 2 10 40 60 100 3 60 5 5 8 2 10 40 60 【输出样例】 Good job,rabbit! What a pity rabbit!
#include <bits/stdc++.h> using namespace std; const int N=105,inf=0x3f3f3f; int l,n,c,t,rv,tv1,tv2,a[N]; double f[N],len,rt,tt; int main() { while(cin>>l){ scanf("%d %d %d",&n,&c,&t); scanf("%d %d %d",&rv,&tv1,&tv2); for(int i=1;i<=n;i++) scanf("%d",&a[i]); a[n+1]=l; rt=l*1.0/rv;//兔子需要花的时间 tt=0; for(int i=1;i<=n+1;i++){ f[i]=inf; for(int j=0;j<i;j++){ len=a[i]-a[j]; double tmp; if(c>=len){ tmp=f[j]+len*1.0/tv1; } else { tmp=f[j]+(len-c)*1.0/tv2+c*1.0/tv1; } if(j)//如果不是刚出发,代表在j充了一次电 tmp+=t; f[i]=min(f[i],tmp); } } tt=f[n+1]; if(rt<tt) printf("Good job,rabbit!\n"); else printf("What a pity rabbit!\n"); } return 0; }
3、拒绝病毒(virus.cpp)
很难,kmp算法与最长公共子序列的结合:kmp算法都还没搞太清楚,代码先放这里
【问题描述】 通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符 串的一个子序列。例如,序列 BDF 是 ABCDEF 的子序列。字符串的子字符串是该字符串的连续子序列。例如,BCD 是 ABCDEF 的子串。你得到了两个字符串 s1,s2 和另一个名为 virus 的字符串。你的任务是找到 s1和 s2 的最长公共子序列,同时不包含 virus 子字符串。 【输入说明】 输入三行,每行一个字符串:s1、s2 和 virus(1 ≤ | s1 |、 | s2 |、 |virus| ≤ 100)。每个字符串仅由大写英文字母组成。 【输出说明】 输出不带病毒的 s1 和 s2 的最长公共子序列。如果有多个答案,任何一个都将被 接受。如果没有有效的公共子序列,则输出 0。 【输入样例 1】 KEAJKEQSLOBSROFGZ ZOVGURWZLWVLUXTH OZ 【输出样例 1】 ORZ 【输入样例 2】 DD DDD D 【输出样例 2】 0
#include <bits/stdc++.h> using namespace std; string a,b,c; int t[105],dp[105][105][105],sa,sb,sc; void kmp(){ int i=0,j=-1; t[0]=-1; for(i=1;i<sc;i++){ while(j>=0&&c[i-1]!=c[j])j=t[j]; j++; t[i]=j; } } int cek(int p,int r){ while(r>=0&&a[p]!=c[r])r=t[r]; return r; } int lcs(int p,int q,int r){ if(r==sc)return -1000; if(p==sa||q==sb)return 0; if(dp[p][q][r]!=-1)return dp[p][q][r]; dp[p][q][r]=max(lcs(p+1,q,r),lcs(p,q+1,r)); if(a[p]==b[q])dp[p][q][r]=max(dp[p][q][r],1+lcs(p+1,q+1,cek(p,r)+1)); return dp[p][q][r]; } int main(){ freopen("virus.in","r",stdin); freopen("virus.out","w",stdout); cin>>a>>b>>c; sa=a.size(); sb=b.size(); sc=c.size(); kmp(); memset(dp,-1,sizeof(dp)); int ans=lcs(0,0,0); if(ans==0)return cout<<"0",0; int p=0,q=0,r=0; while(ans>0){ int gg=cek(p,r)+1; int t1=lcs(p+1,q,r),t2=lcs(p,q+1,r),t3=(a[p]==b[q]?1+lcs(p+1,q+1,gg):-1000); if(t1>=t2&&t1>=t3)p++; else{ if(t2>=t1&&t2>=t3)q++; else{cout<<a[p++];q++;r=gg;ans--;} } } }
4、神秘的礼物(gift.cpp)
相对于第三题就没有这么难了咳咳,其实就像是俄罗斯套娃一样,主要还是注意一下输出
【问题描述】 彼得的朋友要过生日了,彼得决定给他寄一张卡片表示祝贺。为了使他的礼物更 神秘,他决定做一个链子。这里的链是这样一个信封序列 a = {a1,a2,…,an}, 其中第 i 个信封的宽度和高度分别严格高于第(i - 1)个信封的宽度和高度。 链大小是链中信封的数目。 彼得想把他的信封做成最大尺寸的链子。如果卡片的宽度和高度分别低于链中最 小信封的宽度和高度,则卡片能够被装入链中(禁止转动卡片和信封)。 彼得有很多信封和很少的时间,这项艰巨的任务就交给你了。 【输入说明】 第一行包含整数 n,w,h(1 ≤ n ≤ 5000,1 ≤ w, h ≤ 10^6),表示彼得拥有的 信封数量、卡片宽度和高度。接下来是 n 行,每行包含两个整数 wi 和 hi,分别 表示第 i 个信封的宽和高(1 ≤ wi,hi ≤ 10^6)。 【输出说明】 第一行输出最大链大小。 第二行输出满足要求的信封的编号链(用空格隔开),从最小信封的编号开始输 出。请记住,卡片应该放在最小的信封里。 如果最大大小的链不是唯一的,请输出其中任何一个链即可。 如果卡不适合任何信封,请输出 0。 【输入样例 1】 2 1 1 2 2 2 2 【输出样例 1】 1 1 【输入样例 2】 3 3 3 5 4 12 11 9 8 【输出样例 2】 3 1 3 2
#include<iostream> #include<cstring> #include<cstdio> #define N 5005 using namespace std; int w[N],h[N],dp[N],ans[N],n; int f(int v){ if(dp[v]!= 0) return dp[v]; int Max =0,k; for(int i =1; i <= n; i++) if(w[v] < w[i] && h[v] < h[i]){ k = f(i); if(k > Max){ Max = k; ans[v] = i; } } return dp[v] = Max+1; } int main(){ freopen("gift.in","r",stdin); freopen("gift.out","w",stdout); scanf("%d",&n); for(int i = 0; i <= n; i++) scanf("%d%d",&w[i],&h[i]); f(0); cout << dp[0]-1<<endl; for(int i = 0; ans[i]; i = ans[i]) cout<<ans[i]<<" "; return 0; }

浙公网安备 33010602011771号