zoukankan      html  css  js  c++  java
  • 讲义 GDFZOJ 【38】 动态规划基础3

    更好的阅读体验

    上楼梯问题

    这是一个很水的题目,就是一个斐波那契数列……

    设有 (f) 数组,则 (f_i=f_{i-1}+f_{i-2}(f_1=1,f_2=2))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=35;
    int f[N]={0,1,2};
    int main() {
    	int n;
    	scanf("%d",&n);
    	for(int i=3; i<=n; i++) {
    		__________
    	}
    	__________
    	return 0;
    }
    

    乘车与购票

    如果要抵达距离为 (x) 的一个点,可以从 (x-y) 的地方在乘一辆距离为 (y) 的车。

    于是有状态转移方程:(f_i=max{f_{i-j}+a_j}) 。其中 (a) 数组为 (10) 公里及以内的车费。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=105;
    int f[N],a[N];
    int main() {
    	________
    	for(int i=1; i<11; i++) {
    		scanf("%d",a+i);
    	}
    	int n;
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) {
    		for(int j=1; j<11; j++) {
    			________        	
    		}
    	}
    	printf("%d",f[n]);
    	return 0;
    }
    

    小猪过河

    我们可以让这只小猪从 (0) 号节点开始跳,跳到 (n+1) 号节点。

    则有方程:$ f_i=max(f_i,f_{i-j}-q+a_i),1le jle2$ 。

    再判断一下在跳的过程中的体力值即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    int f[N],a[N];
    int main() {
    	int p,q;
    	scanf("%d %d",&p,&q);
    	int n;
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) {
    		scanf("%d",a+i);
    	}
    	f[0]=p,f[1]=p-q>=0?p-q+a[1]:-1e9;
    	for(int i=2; i<=n+1; i++) {
    		int flag=0;
    		________
    	}
    	if(f[n+1]<=0) {
    		printf("NO");
    	} else {
    		printf("%d",f[n+1]);
    	}
    	return 0;
    }
    

    排队买票

    我们可以发现:一个人可以自己买票,也可以和他后面的人买票。

    所以有方程:(f_i=max(f_{i-1}+a_i,f_{i-1}+b_{i-1}))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    int f[N],a[N],b[N];
    int main() {
    	int n;
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) {
    		scanf("%d",a+i);
    	}
    	for(int i=1; i<n; i++) {
    		scanf("%d",b+i);
    	}
    	________
    	printf("%d",f[n]);
    	return 0;
    }
    

    护卫队

    (f_i) 为前 (i) 辆车通过桥的最短时间,设 (t_{i,j})(i) -第 (j) 辆车(租车的车队)通过所需时间。

    我们分析方程:

    来源

    综合起来,我们就得到了方程:(f_i=min(f_{j-1}+t_{j,i}))且要满足 (sum_{k=j}^iw_kle t)

    为了降低时间复杂度、编程复杂度,我们可以:

    1. 预处理重量

      定义 (W_i) 为前 (i) 辆车的总重(即前缀和,是处理这类问题非常好的方法,可以降低时间复杂度)。需要知道第 (j) 辆车到第 (i) 辆车的总重时,用(W_i-W_{j-1})就可以了;

    2. 预处理时间

      定义 (t) 为桥的全长; (v_i) 为第 (i) 辆车的速度; (T_i) :第 (i) 辆车通过桥所需时间; (t_{i,j})(i) 辆车到第 (j) 辆车组成的车队通过桥所需的时间;

    3. (w) 数组、(v) 数组和(W) 数组一定要开 long long,否则两个较大的 int 相加会炸掉! (虽然此题可能没有卡 int)。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    using LL=long long;
    const LL N=1005;
    LL w[N],s[N],sum[N];
    double f[N],T[N],a[N][N];
    int main() {
    	LL t,l,n;
    	scanf("%lld %lld %lld",&t,&l,&n);
    	for(LL i=1; i<=n; i++) {
    		scanf("%lld %lld",w+i,s+i),sum[i]=sum[i-1]+w[i],a[i][i]=T[i]=(double)l/s[i]*60;
    	}
    	for(LL i=1; i<=n-1; i++) {
    		for(LL j=i+1; j<=n; j++) {
    			a[i][j]=max(a[i][j-1],T[j]);
    		}
    	}
    	for(LL i=1; i<=n; i++) {
    		f[i]=T[i]+f[i-1];
    		________
       	}
    	printf("%.1lf",f[n]);
    	return 0;
    }
    

    工作安排

    (f_i) 为前 (i) 分钟能获得的最大的经验值。

    默认在工作后才能获得经验值。容易知道,对于每一个工作,完成时间的 (f) 值就等于开始时间的 (f) 值加上工作所获得的经验值(里面取个最大值),即 (f_{S_i+E_i}=max(f_{S_i+E_i},f_{S_i}+P_i))

    但是要注意,对于第 (i) 分钟,它可能不是某个工作的结束时间,即 (f_i) 不会被更新。所以在枚举完前一个的结束状态时,就要用 (f_j=max(f_j,f_{j-1})) 对结果进行递推。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5005;
    struct work {
    	int s,e,p;
    	void read() {
    		scanf("%d %d %d",&s,&e,&p);
    		e+=s;
    	}
    	bool operator<(const work &x) const {
    		________
    	}
    } a[N];
    int f[N];
    int main() {
    	int n,m;
    	scanf("%d
    %d",&m,&n);
    	for(int i=1; i<=n; i++) {
    		a[i].read();
    	}
    	sort(a+1,a+n+1);
    	________
    	int ans=0;
    	for(int i=1; i<=m; i++) {
    		ans=max(ans,f[i]);
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    最长上升子序列

    首先我们来讲解一下他的递推关系式:(f_i=max(f_i,f_j+1))

    定义 (f_i) 为:以 (a_i) 为末尾的最长上升子序列的长度。

    那么 (f_i) 包含什么呢?

    情况(1):只包含它自己,也就是说它前面的元素全部都比他大;

    情况2:为了保证上升子序列尽可能的长,那么就有 (f_i) 尽可能的大,但是再保证 (f_i) 尽可能大的基础上,还必须满足序列的上升。所以 (f_i=max(1,f_j+1)(j<i,a_j<a_i)) 。这里的 (1) 就是当 (a_i) 前面的数都比他大的时候,他自己为一个子序列;(f_j+1) 指的是: 当第 (i) 个数前面有一个第 (j) 个数满足 (a_j<a_i) 并且 (j<i) 这时候就说明 (a_i) 元素可以承接在 (a_j) 元素后面来尽可能的增加子序列的长度。

    (j)(1) 遍历到 (i-1) ,在这之间,找出尽可能大的 (dp_i)即为最长上升子序列的长度。(注意: (f_n) 不一定是最长的子序列。)

    来源

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1005;
    int a[N],f[N];
    int main() {
    	int n;
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) {
    		scanf("%d",a+i),f[i]=1;
    	}
    	int ans=0;
    	________
    	printf("%d",ans);
    	return 0;
    }
    

    合唱队形

    显然这是一个最长上升子序列最长下降子序列有机结合。

    求出两者可连接的数量的最大值,那 (n) 去减即可。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=205;
    int a[N],f[2][N];
    int main() {
    	int n;
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) {
    		scanf("%d",a+i),f[1][i]=f[0][i]=1;
    	}
    	________
    	int ans=0;
    	for(int i=1; i<=n; i++) {
    		ans=max(f[0][i]+f[1][i]-1,ans);
    	}
    	printf("%d",n-ans);
    	return 0;
    }
    

    友好城市

    将两岸的友好城市用一个结构体存起来,以其中一个城市作为关键字进行排序,那么之后另一个城市其实会呈最长上升子序列,例如 (A) 岸城市 (1)(A)岸城市 (1) 的意思是距离河的起点为 (1)(A) 岸城市)和 (B) 岸城市 (3) 之间如果开通航线,那么以后的城市都不可能向 (B) 岸城市 (1)(2)(3) 开通航线。那么也就呈最长上升子序列状,只要套用模板求出最长即可。

    注意:一定要排序

    来源

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5005;
    struct line {
    	int c1,c2;
    	________
    	void read() {
    		scanf("%d %d",&c1,&c2);
    	}
    } a[N];
    int f[N];
    int main() {
    	int x,y;
    	scanf("%d %d",&x,&y);
    	int n;
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) {
    		a[i].read(),f[i]=1;
    	}
    	sort(a+1,a+n+1);
    	________
    	int ans=0;
    	for(int i=1; i<=n; i++) {
    		ans=max(ans,f[i]);
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    后记

    1. 感谢 L_Z_Y 对此文章提出的建议,并帮助我写了 工作安排 这一块!

    2. 本文有彩蛋

    3. 本文由 Z_Z_R 编写,zhnzh 转载与此。

  • 相关阅读:
    牛客IOI周赛17-提高组 卷积 生成函数 多项式求逆 数列通项公式
    6.3 省选模拟赛 Decompose 动态dp 树链剖分 set
    AtCoder Grand Contest 044 A Pay to Win 贪心
    5.29 省选模拟赛 树的染色 dp 最优性优化
    luogu P6097 子集卷积 FST FWT
    CF724C Ray Tracing 扩展欧几里得 平面展开
    5.30 省选模拟赛 方格操作 扫描线 特殊性质
    5.29 省选模拟赛 波波老师 SAM 线段树 单调队列 并查集
    Spring main方法中怎么调用Dao层和Service层的方法
    Bug -- WebService报错(两个类具有相同的 XML 类型名称 "{http://webService.com/}getPriceResponse"。请使用 @XmlType.name 和 @XmlType.namespace 为类分配不同的名称。)
  • 原文地址:https://www.cnblogs.com/zhnzh/p/13432888.html
Copyright © 2011-2022 走看看