c++动态规划试题+分析
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
4、邮局问题 .cpp/c/pas) (post post.cpp/c/pas) 【问题描述】 一些村庄建在一条笔直的高速公路边上, 我们用一条坐标轴来描述这条公路, 每个村庄 的坐标都是整数,没有两个村庄的坐标相同。两个村庄的距离定义为坐标之差的绝对值。 我
们需要在某些村庄建立邮局。使每个村庄使用与它距离最近的邮局,建立邮局的原则是: 所 有村庄到各自使用的邮局的距离总和最小。 【输入格式】 输入文件共计两行。 第一行两个整数 n 和 m,分别表示有 n 个村庄,需要建立 m 个邮局; 第二行为 n 个整数,表示这 n 个村庄的坐标; 【输出格式】 输出文件仅一行为一个整数,表示最小距离总和。 【样例输入输出】 post.in post.out 10 5 9 1 2 3 6 7 9 11 22 44 50 【数据规模】 1<=村庄数<=300,1<=邮局数<=30,1<=村庄坐标<=10000 【思路点拨】 ①先读入 n 个村庄坐标,按照递增依次排列; ② 求 g[i][j]( 表 示 在 第 a[i] 村 庄 到 a[j] 村 庄 之 间 建 立 一 个 邮 局 的 最 小 距 离 和 , g[i][j]= j | a[k ] − a[ p] | ,(1<=i<=n,i<=k<=j,p=(i+j)/2));
初始值:g[1][i]=g[i][1]=1 时间复杂度为:O(nm) #include<iostream> using namespace std; long long f[105][105]={0},g[105][105]={0},a[105][105],n,m; void init()//读入数据 { int i,j;char c; cin>>n>>m; for(i=1;i<=n;i++) for(j=1;j<=m;j++) { cin>>c; if(c=='1')a[i][j]=1;else a[i][j]=0; } } void DP() { int i,j,x,y; //初始化边界条件 for(i=1;i<=n;i++){f[i][1]=f[i-1][1]+a[i][1];g[i][1]=1;} for(i=1;i<=m;i++){f[1][i]=f[1][i-1]+a[1][i];g[1][i]=1;} for(i=2;i<=n;i++) for(j=2;j<=m;j++) { f[i][j]=max(f[i-1][j],f[i][j-1])+a[i][j];//求最大值 if(f[i][j]==f[i-1][j]+a[i][j])x=1;else x=0; if(f[i][j]==f[i][j-1]+a[i][j])y=1;else y=0; g[i][j]=x*g[i-1][j]+y*g[i][j-1];//求方案数 } cout<<f[n][m]<<endl<<g[n][m]<<endl; } int main() { init(); DP(); } 2、最长上升子序列 .cpp/c/pas) (lis lis.cpp/c/pas) 【问题描述】 LIS 问题是最经典的动态规划基础问题之一。 如果要求一个满足一定条件的最长上升子 序列,你还能解决吗? 给出一个长度为 N 整数序列,请求出它的包含第 K 个元素的最长上升子序列。 例如:对于长度为 6 的序列<2,7,3,4,8,5>,它的最长上升子序列为 <2,3,4,5>,但如果限 制一定要包含第 2 个元素,那么满足此要求的最长上升子序列就只能是<2,7,8>了。 【输入格式】 第一行为两个整数 N,K,如上所述。 接下来是 N 个整数,描述一个序列。 【输出格式】 请输出两个整数,即包含第 K 个元素的最长上升子序列长度。 【样例输入输出】 lis.in lis.out 86 4 65 158 170 299 300 155 207 389 【数据范围】 对于 60%的数据,满足 0<n<=4000,0<k<=n 对于 100%的数据,满足 0<n<=200000,0<k<=n #include<iostream>
100%的数据中 N ,M≤100000,Ti,V i≤10。 【题目考点】01 背包+多重背包 【思路点拨】题面虽然是最直接的 01 背包问题,但根据数据规模只能得 50 分。注意到 T 和 V 都不超不过 10 ,于是物品的种类不会超过 121。于是可以采用多重背包的算法解决该 题。 #include<fstream> using namespace std; ifstream cin("medic.in"); ofstream cout("medic.out"); int N,M,T[105],V[105],amt[105],cnt=0,f[100005],sum[11][11]; void init() { int i,j,t,v; cin>>N>>M; for(i=1;i<=N;i++){cin>>t>>v;sum[t][v]++;}//sum[t][v]时间为 t,价格为 v 的草药数量 for(i=1;i<=10;i++) for(j=1;j<=10;j++) if(sum[i][j]) { amt[++cnt]=sum[i][j];//数量 T[cnt]=i;//时间 V[cnt]=j;//价格 } } void Complete(int v,int t) { for(int i=t;i<=M;i++) if(f[i-t]+v>f[i])f[i]=f[i-t]+v; } void ZeroOne(int v,int t) { for(int i=M;i>=t;i--) if(f[i-t]+v>f[i])f[i]=f[i-t]+v; } void Mult_Pack() { for(int i=1;i<=cnt;i++) { if(amt[i]*T[i]>M){Complete(V[i],T[i]);continue;} int k=1; while(k<=amt[i]) { ZeroOne(V[i]*k,T[i]*k); amt[i]-=k; k*=2; } if(amt[i])ZeroOne(V[i]*amt[i],T[i]*amt[i]); } } int main() { init(); Mult_Pack(); cout<<f[M]<<endl; return 0; }
∑
k =i
wenku.baidu.com
③设 f[i][j] :表示在前 j 个村庄建立 i 个邮局所得的最小距离和。 f[1][i]=g[1][i]; f[i][j]=min{f[i-1][k]+g[k+1][j]},(2<=i<=m,i<=j<=n,i-1<=k<=j-1) #include<iostream> #include<cstdio> #include<cstring> #include <algorithm> using namespace std; int a[1005],n,m,sum[1005],g[1005][1005],f[1005][1005]; void Init() { int i,j,k,L; //n 个村庄,m 个邮局 cin>>n>>m; cin>>n>>m;//n for(i=1;i<=n;i++)cin>>a[i]; sort(a+1,a+n+1); //先按照距离从小到大快排 for(i=1;i<=n-1;i++) //初始化 g[i][j] { g[i][i]=0; for(j=i+1;j<=n;j++)g[i][j]=g[i][j-1]+abs(a[j]-a[(i+j)/2]); } } void DP() { int i,j,k,Min; for(i=1;i<=n;i++)f[1][i]=g[1][i]; for(i=2;i<=m;i++) //阶段:邮局 for(j=i;j<=n;j++) //状态:村庄 { Min=0x7fffffff/2; for(k=i-1;k<=j-1;k++) if(Min>f[i-1][k]+g[k+1][j])Min=f[i-1][k]+g[k+1][j]; f[i][j]=Min; } cout<<f[m][n]<<endl; } int main() { Init(); DP(); system("pause"); }
动态规划模拟试题 5: 10) (时间:1:40—— ——5: 5:1 1、拾垃圾的机器人 (robit.cpp/c/pas) 【问题描述】 有一块地被划分成了 n*m 个区域,在一些区域里有垃圾要拾捡。现在科研人员开发了 一个能捡垃圾的机器人, 机器人每一次都可以移动一个区域的距离, 假设机器人从最左上区 域出发, 他每次只能向右或者向下走。 每次他到达一个点, 就会自动把这个点内的垃圾拾掉。 问:该机器人最多能够拾多少垃圾? 在最多情况下,有多少种方案? 【输入格式】 输入文件的第一行为两个整数 n 和 m; 接下来有一个 n*m 的 01 矩阵。 矩阵中的第 i 行 j 列的数字 a[i][j]=0 表示为空地, a[i][j]=1 表示为垃圾。 【输出格式】 输出文件的第一行为一个整数,表示该机器人拾到的最多垃圾数。 第二行为一个整数,表示该机器人拾到的最多垃圾的路径总数。 【样例输入输出 1】 robit.in robit.out 33 2 100 3 000 010 【样例输入输出 2】 robit.in robit.out 67 5 0101000 11 0001010 0000000 0001001 0000000 0000010 【数据范围】n<=100,m<=100 【思路点拨】我们先看样例,如图 1,垃圾用 G 表示。假设机器人走到(i,j) 这个位置,由于 机器人只能向下向右走,那么机器人的上一个位置一定是(i-1,j)和(i,j-1)。
我们设机器人走到(i,j) 位置时拾到最多垃圾数为 f[i][j] ,由于机器人只能朝右和下走, 只 会跟机器人上一位置拾到最多的垃圾数有关,因此很容易写出状态转移方程。 F[i][j]=max{f[i-1][j],f[i][j-1]}+a[i][j],(1<=i<=n,1<=j<=m) 初始值:f[1][i]=f[1][i-1]+a[1][i] ,f[i][1]=f[i-1][1]+a[i][1] 时间复杂度为:O(nm) 我们再来看第二问: 在最优解的情况下求方案总数, 我们只要每次在最优解的情况下统 计路径条数即可,见图 2: 设 g[i][j] 表示在位置(i,j)达到拾到 f[i][j]垃圾时的路径总数,有如下方程: g[i][j]=g[i-1][j]*x+g[i][j-1]*y 当 f[i][j]=f[i-1][j]+a[i][j] 时,x=1,否则 x=0; 当 f[i][j]=f[i][j-1]+a[i][j] 时,y=1,否则 y=0;
using namespace std; int n,m,a[200005],b[200005],f[200005]; void init() { int i,j=0; cin>>n>>m; for(i=1;i<=n;i++)cin>>a[i]; for(i=1;i<m;i++)if(a[i]<a[m])b[++j]=a[i];//去掉前面大于等于 a[m]的数 b[++j]=a[m]; for(i=m+1;i<=n;i++)if(a[i]>a[m])b[++j]=a[i];//去掉后面小于等于 a[m]的数 } int ERLIS()//上升子序列 { int i,L,r,mid,len=1; f[1]=b[1]; for(i=2;i<=n;i++) { L=1;r=len; if(f[len]<b[i]){len++;f[len]=b[i];continue;} while(L<=r) { mid=(L+r)/2; if(f[mid]<b[i])L=mid+1;else r=mid-1; } f[L]=b[i]; } return len; } int main() { init(); cout<<ERLIS()<<endl; } 3、采药 .cpp/c/pas) (medic medic.cpp/c/pas) 【问题描述】 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最 有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都 是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间, 每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果 你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗? 【输入格式】 输入文件的第一行包含两个正整数 N,M。M 表示总共能够用来采药的时间,N 代表山 洞里的草药的数目。 接下来的 N 行每行包括两个的整数,分别表示采摘某株草药的时间 T i 和这株草药的价 值 V i。 【输出格式】 输出文件仅包含一个整数表示规定时间内可以采到的草药的最大总价值。 【样例输入输出】 medic .in medic .out medic.in medic.out 39 3 10 10 81 12 】 【数据规模和约定 数据规模和约定】 50%的数据中 N ,M≤1000;