6.22 集训--DP复习一
总结
下午的突击练习完全不在状态
A、拦截导弹简单版
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
输入只有一行,为若干个正整数,一次为导弹的高度。
输出格式
第一行为最多能拦截的导弹数;
第二行为要拦截所有导弹最少要配备的系统数
样例
样例输入
389 207 155 300 299 170 158 65
样例输出
6
2
分析
这里要用到一个定理----最小链划分=最大反链长度
所以我们分别正着、倒着跑一遍LIS就可以了
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int f1[maxn],f2[maxn],a[maxn],b[maxn];
int cnt=1;
int main(){
while(scanf("%d",&a[cnt])!=EOF) cnt++;
cnt--;
if(cnt==0){
printf("0\n0\n");
return 0;
}
for(int i=1;i<=cnt;i++) scanf("%d",&a[i]);
for(int i=1;i<=cnt;i++) b[i]=a[cnt-i+1];
int ans1=1,ans2=1;
for(int i=1;i<=cnt;i++){
for(int j=0;j<i;j++){
if(a[i]>a[j]){
f1[i]=max(f1[j]+1,f1[i]);
}
if(b[i]>=b[j]){
f2[i]=max(f2[j]+1,f2[i]);
}
ans1=max(ans1,f1[i]);
ans2=max(ans2,f2[i]);
}
}
printf("%d\n%d\n",ans2,ans1);
return 0;
}
B、挖地雷
题目描述
在一个地图上有N个地窖(N<=200),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径,并规定路径都是单向的,也不存在可以从一个地窖出发经过若干地窖后又回到原来地窖的路径。
某人可以从任一处开始挖地雷,然后沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使他能挖到最多的地雷。
输入格式
第一行一个整数n表示有n个地窖
第二行有n个整数表示每个地窖的地雷数
以下有若干行,每行有两个数x,y表示x可以到y,保证x小于y。
最后一行有两个0,表示输入结束
输出格式
第一行输出挖地雷的顺序。
第二行为最多挖出的地雷数
样例
样例输入
6
5 10 20 5 4 5
1 2
1 4
2 4
3 4
4 5
4 6
5 6
0 0
样例输出
3-4-5-6
34
分析
乍看上去要用SPFA以每一个点为起点跑一遍最长路
不过SPFA的时间效率很不稳定,会T,因此我们考虑别的方法
因为题目中的x一定是小于y的,所以我们可以像像线性DP那样定义f\([j]\)为挖到编号为\(j\)的地窖的最大地雷数
如果\(vis[j][i]==1\),那么\(f[i]=max(f[i],f[j]+a[i])\)
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
bool vis[maxn][maxn];
int a[maxn],f[maxn],jl[maxn];
int du[maxn];
int cnt=0;
int c[maxn];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int aa,bb;
while(scanf("%d%d",&aa,&bb)!=EOF && aa!=0){
vis[aa][bb]=1;
du[bb]++;
}
for(int i=1;i<=n;i++){
if(du[i]==0) f[i]=a[i];
}
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
if(vis[j][i]){
if(f[i]<f[j]+a[i]){
f[i]=f[j]+a[i];
jl[i]=j;
}
}
}
}
int now=0,ans=0;
for(int i=1;i<=n;i++){
if(f[i]>ans){
ans=f[i];
now=i;
}
}
c[++cnt]=now;
while(jl[now]){
c[++cnt]=jl[now];
now=jl[now];
}
for(int i=cnt;i>=1;i--){
if(i==cnt) printf("%d",c[i]);
else printf("-%d",c[i]);
}
printf("\n%d\n",ans);
return 0;
}
C. 飞翔
题目描述
鹰最骄傲的就是翱翔,但是鹰们互相都很嫉妒别的鹰比自己飞的快,更嫉妒其他的鹰比自己飞行的有技巧。于是,他们决定举办一场比赛,比赛的地方将在一个迷宫之中。
这些鹰的起始点被设在一个N*M矩阵的左下角map[1,1]的左下角。终点被设定在矩阵的右上角map[N,M]的右上角,有些map[i,j]是可以从中间穿越的。每一个方格的边长都是100米。如图所示:
没有障碍,也没有死路。这样设计主要是为了高速飞行的鹰们不要发现死路来不及调整而发生意外。
潘帕斯雄鹰冒着减RP的危险从比赛承办方戒备森严的基地中偷 来了施工的地图。但是问题也随之而来,他必须在比赛开始之前把地图的每一条路都搞清楚,从中找到一条到达终点最近的路。(哈哈,笨鸟不先飞也要拿冠军)但 是此鹰是前无古鹰,后无来鹰的吃菜长大的鹰--菜鸟。他自己没有办法得出最短的路径,于是紧急之下找到了学OI的你,希望找到你的帮助。
输入格式
首行为n,m(0<n,m<=1000000),第2行为k(0<k<=1000)表示有多少个特殊的边。以下k行为两个数,i,j表示map[i,j]是可以直接穿越的。
输出格式
仅一行,(1,1)-->(n,m)的最短路径的长度,四舍五入保留到整数即可
样例
样例输入
3 2
3
1 1
3 2
1 2
样例输出
383
分析
我们肯定要尽量使穿过对角线的边更多
所以我们把特殊边的横纵坐标存到结构体里,按照横坐标从小到大排序
然后用纵坐标跑一个LIS,得到的结果就是最多可以经过的特殊边的个数
那么最后的答案就是特殊边的个数\(\times \sqrt{200000} +\)普通边的个数\(\times 100\)
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int n,m,k;
struct asd{
int x,y;
}b[maxn];
bool cmp(asd aa,asd bb){
if(aa.x==bb.x) return aa.y<bb.y;
return aa.x<bb.x;
}
int f[maxn];
int ans=0;
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++){
scanf("%d%d",&b[i].x,&b[i].y);
}
sort(b+1,b+1+k,cmp);
for(int i=1;i<=k;i++){
for(int j=0;j<i;j++){
if(b[i].y>b[j].y && b[i].x!=b[j].x) f[i]=max(f[i],f[j]+1);
}
ans=max(ans,f[i]);
}
int tot=m+n-ans*2;
double mans=(double)ans*sqrt(20000)+(double)tot*100.0;
printf("%.0lf\n",mans);
return 0;
}
D. 麻烦的聚餐
题目描述
为了避免餐厅过分拥挤,FJ要求奶牛们分3批就餐。每天晚饭前,奶牛们都会在餐厅前排队入内,按FJ的设想,所有第3批就餐的奶牛排在队尾,队伍的前端由设定为第1批就餐的奶牛占据,中间的位置就归第2批就餐的奶牛了。
由于奶牛们不理解FJ的安排,晚饭前的排队成了一个大麻烦。
第i头奶牛有一张标明她用餐批次的卡片\(Di(1 \leq D_{i} \leq 3)\)。虽然所有 \(N (1 \leq N \leq 30000)\)头奶牛排成了很整齐的队伍,但谁都看得出来,卡片上的号码是完全杂乱无章的。
在若干次混乱的重新排队后,FJ找到了一种简单些的方法:
奶牛们不动,他沿着队伍从头到尾走一遍,把那些他认为排错队的奶牛卡片上的编号改 掉,最终得到一个他想要的每个组中的奶牛都站在一起的队列.
例如111222333或者333222111。
哦,你也发现了,FJ不反对一条前后颠倒的队列,那样他可以让所有奶牛向后转,然后按正常顺序进入餐厅。
你也晓得,FJ是个很懒的人。他想知道,如果他想达到目的,那么他最少得改多少头奶牛卡片上的编号。所有奶牛在FJ改卡片编号的时候,都不会挪位置
输入格式
第1行: 1个整数:N
第2..N+1行: 第i+1行是1个整数\(D_{i}\),为第i头奶牛的用餐批次
输出格式
第1行: 输出1个整数,为FJ最少要改几头奶牛卡片上的编号,才能让编号变成他设想中的样子
样例
样例输入
5
1
3
2
1
1
样例输出
1
数据范围与提示
【输入说明】
队列中共有5头奶牛,第1头以及最后2头奶牛被设定为第一批用餐,第2头奶牛的预设是第三批用餐,第3头则为第二批用餐。
【输出说明】
如果FJ想把当前队列改成一个不下降序列,他至少要改2头奶牛的编号,一种可行的方案是:
把队伍中2头编号不是1的奶牛的编号都改成1。
不过,如果FJ选择把第1头奶牛的编号改成3就能把奶牛们的队伍改造成一个合法的不上升序列了。
分析
这一道题N很大,要用到LIS的优化
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=30010;
int a[maxn],b[maxn],c[maxn],d[maxn];
int lb(int x){
return x&-x;
}
void ad(int x,int da){
for(int i=x;i<maxn;i+=lb(i)){
c[i]=max(c[i],da);
}
}
int he(int x){
int mmax=0;
for(int i=x;i;i-=lb(i)){
mmax=max(mmax,c[i]);
}
return mmax;
}
void ad1(int x,int da){
for(int i=x;i<maxn;i+=lb(i)){
d[i]=max(d[i],da);
}
}
int he1(int x){
int mmax=0;
for(int i=x;i;i-=lb(i)){
mmax=max(mmax,d[i]);
}
return mmax;
}
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[n-i+1]=a[i];
}
int ans=0;
for(int i=1;i<=n;i++){
c[a[i]]=he(a[i])+1;
ans=max(ans,c[a[i]]);
ad(a[i],c[a[i]]);
}
for(int i=1;i<=n;i++){
d[b[i]]=he1(b[i])+1;
ans=max(ans,d[b[i]]);
ad1(b[i],d[b[i]]);
}
printf("%d\n",n-ans);
return 0;
}
E. 奶牛渡河
题目描述
Farmer John以及他的N(1 <= N <= 2,500)头奶牛打算过一条河,但他们所有的渡河工具,仅仅是一个木筏。
由于奶牛不会划船,在整个渡河过程中,FJ必须始终在木筏上。在这个基础上,木筏上的奶牛数目每增加1,FJ把木筏划到对岸就得花更多的时间。
当FJ一个人坐在木筏上,他把木筏划到对岸需要M(1 <= M <= 1000)分钟。
当木筏搭载的奶牛数目从i-1增加到i时,FJ得多花分钟才能把木筏划过河
也就是说,船上有1头奶牛时,FJ得花分钟渡河;船上有2头奶牛时,时间就变成分钟。后面 的依此类推。
那么,FJ最少要花多少时间,才能把所有奶牛带到对岸呢?当然,这个时间得包括FJ一个人把木筏从对岸划回来接下一批的奶牛的时间。
输入格式
第1行: 2个用空格隔开的整数:N 和 M
第2..N+1行: 第i+1为1个整数:
输出格式
第1行: 输出1个整数,为FJ把所有奶牛都载过河所需的最少时间
样例
样例输入
5 10
3
4
6
100
1
样例输出
50
数据范围与提示
【输入说明】
FJ带了5头奶牛出门。如果是单独把木筏划过河,FJ需要花10分钟,带上1头奶牛的话,是13分钟,2头奶牛是17分钟,3头是23分钟,4头是123分钟,将5头一次性载过去,花费的时间是124分钟。
【输出说明】
Farmer John第一次带3头奶牛过河(23分钟),然后一个人划回来(10分钟),最后带剩下的2头奶牛一起过河(17分钟),总共花费的时间是23+10+17 = 50分钟。
分析
我们设\(sum[i]\)为\(FJ\)把\(i\)头奶牛运到对岸自己又回来的花费,设\(f[i]\)为运输\(i\)头奶牛的最小花费
于是\(f[i]=min(f[i],f[j]+sum[i-j]);\)
最后得到的答案还要减去\(m\),因为\(FJ\)最后一次送奶牛过河不需要再回到出发点
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2505;
typedef long long iint;
iint sum[maxn],f[maxn];
int main(){
int n,m;
scanf("%d%d",&n,&m);
sum[0]=m*2;
f[0]=m*2;
for(int i=1;i<=n;i++){
iint aa;
scanf("%lld",&aa);
sum[i]=sum[i-1]+aa;
f[i]=sum[i];
}
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
f[i]=min(f[i],f[j]+sum[i-j]);
}
}
printf("%lld\n",f[n]-m);
return 0;
}
F. 机器分配
题目描述
总公司拥有高效设备M台, 准备分给下属的N个分公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M <= 15,N <= 10。分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数M。
输入格式
第1行有两个数,第一个数是分公司数N,第二个数是设备台数M。
接下来是一个N*M的矩阵,表明了第i个公司分配j台机器的盈利。
输出格式
第1行输出最大盈利值。
接下来N行,每行2个数,即分公司编号和该分公司获得设备台数。
样例
样例输入
?> 3 3
30 40 50
20 30 50
20 25 30
样例输出
70
1 1
2 1
3 1
分析
我们设\(f[i][j]\)为前\(i\)个公司分配\(j\)台机器的最大利润
那么\(f[i][j]=f[i-1][j-k]+a[i][k]\)
其中\(0 \leq k \leq j\)
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2505;
int a[maxn][maxn],f[maxn][maxn],jl[maxn][maxn];
int cnt;
struct asd{
int aa,bb;
}b[maxn];
bool cmp(asd cc,asd dd){
return cc.aa<dd.aa;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<=j;k++){
if(f[i][j]<=f[i-1][j-k]+a[i][k]){
f[i][j]=f[i-1][j-k]+a[i][k];
jl[i][j]=k;
}
}
}
}
printf("%d\n",f[n][m]);
while(n>0){
b[++cnt].aa=n;
b[cnt].bb=jl[n][m];
m=m-jl[n][m];
n--;
}
sort(b+1,b+1+cnt,cmp);
for(int i=1;i<=cnt;i++){
printf("%d %d\n",b[i].aa,b[i].bb);
}
return 0;
}
6.22 集训--DP复习一的更多相关文章
- 集训DP复习整理
DP复习 集训%你赛2:测绘(审题DP) 经过2000+个小时的努力终于把这道题做出来的蒟蒻通 分析: 这道题我一直没做出来的原因就是因为我太蒟了题面看不懂,题面读懂了,其实不是特别难. 题目翻译: ...
- 区间DP复习
区间DP复习 (难度排序:(A,B),(F,G,E,D,H,I,K),(C),(J,L)) 这是一个基本全在bzoj上的复习专题 没有什么可以说的,都是一些基本的dp思想 A [BZOJ1996] [ ...
- 状压DP复习
深感自己姿势水平之蒻……一直都不是很会状压DP,NOIP又特别喜欢考,就来复习一发…… 题目来源 Orz sqzmz T1 [BZOJ4197][NOI2015]寿司晚宴 (做过)质因数分解最大的质因 ...
- 状压DP复习笔记
前言 复习笔记第4篇.CSP RP++. 引用部分为总结性内容. 0--P1433 吃奶酪 题目链接 luogu 题意 房间里放着 \(n\) 块奶酪,要把它们都吃掉,问至少要跑多少距离?一开始在 \ ...
- 斜率优化DP复习笔记
前言 复习笔记2nd. Warning:鉴于摆渡车是普及组题目,本文的难度定位在普及+至省选-. 参照洛谷的题目难度评分(不过感觉部分有虚高,提高组建议全部掌握,普及组可以选择性阅读.) 引用部分(如 ...
- 矩阵乘法优化DP复习
前言 最近做毒瘤做多了--联赛难度的东西也该复习复习了. Warning:本文较长,难度分界线在"中场休息"部分,如果只想看普及难度的可以从第五部分直接到注意事项qwq 文中用(比 ...
- 数位DP复习笔记
前言 复习笔记第五篇.(由于某些原因(见下),放到了第六篇后面更新)CSP-S RP++. luogu 的难度评级完全不对,所以换了顺序,换了别的题目.有点乱,见谅.要骂就骂洛谷吧,原因在T2处 由于 ...
- NOIP 考前DP 复习
POJ 2533 最长不降子序列 #include <cstdio> ; int a[Maxn],Pos[Maxn],F[Maxn],n,Ans; inline int Max(int x ...
- 树形DP 复习
树形DP 树形DP:建立在树上的动态规划 一般有两种传递方式:根→叶或叶→根 前者出现在换根DP中,一般操作是求出某一个点的最优解,再通过这一个点推知其他点的最优解. 后者是树形DP的常见形式,一般树 ...
随机推荐
- shell中文本内容多行变一行的技巧
在linux下有时可能需要将多行的值转成一行.其实现的方法有很多种.笔者将自己曾经用过的方法在些分享. 如有一文本文件5201351.txt,文本的内容如下: 现我们可以通过如下方法将文本内容转成一行 ...
- iOS-UIViewController创建的几种方法和UIWindow的介绍
在上一篇笔记中<iOS-程序启动原理和UIApplication>,http://blog.csdn.net/yang198907/article/details/49735531 在程序 ...
- [每日一题2020.06.13]leetcode #739 #15 单调栈 双指针查找
739 每日温度 ( 单调栈 ) 题目 : https://leetcode-cn.com/problems/daily-temperatures/ 题意 : 找到数组每一个元素之后第一个大于它的元素 ...
- TensorFlow从0到1之浅谈深度学习(10)
DNN(深度神经网络算法)现在是AI社区的流行词.最近,DNN 在许多数据科学竞赛/Kaggle 竞赛中获得了多次冠军. 自从 1962 年 Rosenblat 提出感知机(Perceptron)以来 ...
- Python爬虫小白入门(一)入门介绍
一.前言 你是不是在为想收集数据而不知道如何收集而着急? 你是不是在为想学习爬虫而找不到一个专门为小白写的教程而烦恼? Bingo! 你没有看错,这就是专门面向小白学习爬虫而写的!我会采用实例的方式, ...
- CentOS7.5搭建Hadoop2.7.6完全分布式集群
一 完全分布式集群搭建 Hadoop官方地址:http://hadoop.apache.org/ 1 准备3台客户机 1.2 关闭防火墙,设置静态IP,主机名 关闭防火墙,设置静态IP,主机名此处略 ...
- (九)maven-surefire-plugin常用配置
原文链接:https://www.cnblogs.com/pixy/p/4718176.html 对maven-surefire-plugin有想了解的,看这篇:https://www.cnblogs ...
- tkinter操作练习一
# -*- utf-8 -*- #@Time :2019/8/2723:17 #@Author :无邪 #@File :t_ui.py #@Software:PyCharm import tkinte ...
- Dotnet core基于ML.net的销售数据预测实践
ML.net已经进到了1.5版本.作为Microsoft官方的机器学习模型,你不打算用用? 一.前言 ML.net可以让我们很容易地在各种应用场景中将机器学习加入到应用程序中.这是这个框架很重要的 ...
- 【JMeter_14】JMeter逻辑控制器__交替控制器<Interleave Controller>
交替控制器<Interleave Controller> 业务逻辑: 根据被控制器触发执行次数,去依次执行控制器下的子节点<逻辑控制器.采样器>. 被触发执行可以由线程组的线程 ...