9.7做题随记
主题:贪心
P5019 [NOIP 2018 提高组] 铺设道路
KEY:注意数列与子序列的单调性,常常与贪心有关
题目背景
NOIP2018 提高组 D1T1
题目描述
春春是一名道路工程师,负责铺设一条长度为 \(n\) 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 \(n\) 块首尾相连的区域,一开始,第 \(i\) 块区域下陷的深度为 \(d_i\) 。
春春每天可以选择一段连续区间 \([L,R]\) ,填充这段区间中的每块区域,让其下陷深度减少 \(1\)。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 \(0\) 。
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 \(0\) 。
输入格式
输入文件包含两行,第一行包含一个整数 \(n\),表示道路的长度。 第二行包含 \(n\) 个整数,相邻两数间用一个空格隔开,第 \(i\) 个整数为 \(d_i\) 。
输出格式
输出文件仅包含一个整数,即最少需要多少天才能完成任务。
输入输出样例 #1
输入 #1
6
4 3 2 5 3 5
输出 #1
9
说明/提示
【样例解释】
一种可行的最佳方案是,依次选择:
\([1,6]\)、\([1,6]\)、\([1,2]\)、\([1,1]\)、\([4,6]\)、\([4,4]\)、\([4,4]\)、\([6,6]\)、\([6,6]\)。
【数据规模与约定】
对于 \(30\%\) 的数据,\(1 ≤ n ≤ 10\) ;
对于 \(70\%\) 的数据,\(1 ≤ n ≤ 1000\) ;
对于 \(100\%\) 的数据,\(1 ≤ n ≤ 100000 , 0 ≤ d_i ≤ 10000\) 。
分析
观察这里数据
6
4 3 2 5 3 5
可以发现,不能一次性处理的是出现0的情况,出现0会把数列分成好几份
那如何保证不出现好几份的请况?
只要数组单调不增即可,如4 3 2 2 2 2
当然,这是理想情况,如果像这里4 3 2 5,我们就要多花d[i]-d[i-1]去填,就是5-2=3,
再到后面如0 0 0 3 1 3,又可以此类推
代码
#include<bits/stdc++.h>
using namespace std;
const int U=1e5+5;
int n,d[U];
int main()
{
#ifndef ONLINE_JUDGE
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
int cnt=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>d[i];
for(int i=1;i<=n;i++)
if(d[i]>d[i-1]) cnt+=d[i]-d[i-1];
cout<<cnt<<endl;
return 0;
}
// 编译常用指令>> g++ -std=c++14 -O2 -Wall code.cpp -o code
/*
6
4 3 2 5 3 5
*/
P3817 小A的糖果
KEY:注意数据范围,不开Longlong见祖宗
题目描述
小 A 有 \(n\) 个糖果盒,第 \(i\) 个盒中有 \(a_i\) 颗糖果。
小 A 每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中糖的个数之和都不大于 \(x\),至少得吃掉几颗糖。
输入格式
输入的第一行是两个用空格隔开的整数,代表糖果盒的个数 \(n\) 和给定的参数 \(x\)。
第二行有 \(n\) 个用空格隔开的整数,第 \(i\) 个整数代表第 \(i\) 盒糖的糖果个数 \(a_i\)。
输出格式
输出一行一个整数,代表最少要吃掉的糖果的数量。
输入输出样例 #1
输入 #1
3 3
2 2 2
输出 #1
1
输入输出样例 #2
输入 #2
6 1
1 6 1 2 0 4
输出 #2
11
输入输出样例 #3
输入 #3
5 9
3 1 4 1 5
输出 #3
0
说明/提示
样例输入输出 1 解释
吃掉第 2 盒中的一个糖果即可。
样例输入输出 2 解释
第 2 盒糖吃掉 \(6\) 颗,第 4 盒吃掉 \(2\) 颗,第 6 盒吃掉 \(3\) 颗。
数据规模与约定
- 对于 \(30\%\) 的数据,保证 \(n \leq 20\),\(a_i, x \leq 100\)。
- 对于 \(70\%\) 的数据,保证 \(n \leq 10^3\),\(a_i, x \leq 10^5\)。
- 对于 \(100\%\) 的数据,保证 \(2 \leq n \leq 10^5\),\(0 \leq a_i, x \leq 10^9\)。
Code
#include<bits/stdc++.h>
using namespace std;
const int U=1e5+5;
long long n,x;
long long a[U];
int main()
{
#ifndef ONLINE_JUDGE
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
long long cnt=0;
cin>>n>>x;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
if(a[i-1]+a[i]>x) cnt+=a[i-1]+a[i]-x,a[i]-=a[i-1]+a[i]-x;
}
cout<<cnt<<endl;
return 0;
}
// 编译常用指令>> g++ -std=c++14 -O2 -Wall code.cpp -o code
/*
6
4 3 2 5 3 5
*/
P1208 [USACO1.3] 混合牛奶 Mixing Milk
题目描述
由于乳制品产业利润很低,所以降低原材料(牛奶)价格就变得十分重要。帮助 Marry 乳业找到最优的牛奶采购方案。
Marry 乳业从一些奶农手中采购牛奶,并且每一位奶农为乳制品加工企业提供的价格可能相同。此外,就像每头奶牛每天只能挤出固定数量的奶,每位奶农每天能提供的牛奶数量是一定的。每天 Marry 乳业可以从奶农手中采购到小于或者等于奶农最大产量的整数数量的牛奶。
给出 Marry 乳业每天对牛奶的需求量,还有每位奶农提供的牛奶单价和产量。计算采购足够数量的牛奶所需的最小花费。
注:每天所有奶农的总产量不少于 Marry 乳业的需求量。
输入格式
第一行二个整数 \(n,m\),表示需要牛奶的总量,和提供牛奶的农民个数。
接下来 \(m\) 行,每行两个整数 \(p_i,a_i\),表示第 \(i\) 个农民牛奶的单价,和农民 \(i\) 一天最多能卖出的牛奶量。
输出格式
单独的一行包含单独的一个整数,表示 Marry 的牛奶制造公司拿到所需的牛奶所要的最小费用。
输入输出样例 #1
输入 #1
100 5
5 20
9 40
3 10
8 80
6 30
输出 #1
630
说明/提示
【数据范围】
对于 \(100\%\) 的数据:
\(0 \le n,a_i \le 2 \times 10^6\),\(0\le m \le 5000\),\(0 \le p_i \le 1000\)
题目翻译来自 NOCOW。
USACO Training Section 1.3
Code
#include<bits/stdc++.h>
using namespace std;
const int U=2e6+5;
int n,m;
struct node
{
long long p,a;
}f[U];
int cmp(node x,node y)
{
return x.p<y.p;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
int cost=0;
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>f[i].p>>f[i].a;
sort(f+1,f+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(n==0) break;
if(n>=f[i].a) cost+=f[i].a*f[i].p,n-=f[i].a;
else cost+=n*f[i].p,n=0;
}
cout<<cost<<endl;
return 0;
}
// 编译常用指令>> g++ -std=c++14 -O2 -Wall code.cpp -o code
/*
6
4 3 2 5 3 5
*/
P1094 [NOIP 2007 普及组] 纪念品分组
双指针
题目背景
NOIP2007 普及组 T2
题目描述
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得 的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品, 并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。
你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
输入格式
共 \(n+2\) 行:
第一行包括一个整数 \(w\),为每组纪念品价格之和的上限。
第二行为一个整数 \(n\),表示购来的纪念品的总件数 \(G\)。
第 \(3\sim n+2\) 行每行包含一个正整数 \(P_i\) 表示所对应纪念品的价格。
输出格式
一个整数,即最少的分组数目。
输入输出样例 #1
输入 #1
100
9
90
20
20
30
50
60
70
80
90
输出 #1
6
说明/提示
\(50\%\) 的数据满足:\(1\le n\le15\)。
\(100\%\) 的数据满足:\(1\le n\le3\times10^4\),\(80\le w\le200\),\(5 \le P_i \le w\)。
Code
#include<bits/stdc++.h>
using namespace std;
const int U=3e4+5;
int w,n,p[U];
int main()
{
#ifndef ONLINE_JUDGE
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
int cnt=0;
cin>>w>>n;
for(int i=1;i<=n;i++) cin>>p[i];
sort(p+1,p+n+1);
for(int i=n,j=1;i>=j;)
{
if(p[i]+p[j]<=w) i--,j++,cnt++;
else i--,cnt++;
}
cout<<cnt<<endl;
return 0;
}
// 编译常用指令>> g++ -std=c++14 -O2 -Wall code.cpp -o code
/*
6
4 3 2 5 3 5
*/
P4995 跳跳!
题目描述
你是一只小跳蛙,你特别擅长在各种地方跳来跳去。
这一天,你和朋友小 F 一起出去玩耍的时候,遇到了一堆高矮不同的石头,其中第 \(i\) 块的石头高度为 \(h_i\),地面的高度是 \(h_0 = 0\)。你估计着,从第 \(i\) 块石头跳到第 \(j\) 块石头上耗费的体力值为 \((h_i - h_j) ^ 2\),从地面跳到第 \(i\) 块石头耗费的体力值是 \((h_i) ^ 2\)。
为了给小 F 展现你超级跳的本领,你决定跳到每个石头上各一次,并最终停在任意一块石头上,并且小跳蛙想耗费尽可能多的体力值。
当然,你只是一只小跳蛙,你只会跳,不知道怎么跳才能让本领更充分地展现。
不过你有救啦!小 F 给你递来了一个写着 AK 的电脑,你可以使用计算机程序帮你解决这个问题,万能的计算机会告诉你怎么跳。
那就请你——会写代码的小跳蛙——写下这个程序,为你 NOIp AK 踏出坚实的一步吧!
输入格式
输入一行一个正整数 \(n\),表示石头个数。
输入第二行 \(n\) 个正整数,表示第 \(i\) 块石头的高度 \(h_i\)。
输出格式
输出一行一个正整数,表示你可以耗费的体力值的最大值。
输入输出样例 #1
输入 #1
2
2 1
输出 #1
5
输入输出样例 #2
输入 #2
3
6 3 5
输出 #2
49
说明/提示
样例解释
两个样例按照输入给定的顺序依次跳上去就可以得到最优方案之一。
数据范围
对于 \(1 \leq i \leq n\),有 \(0 < h_i \leq 10 ^ 4\),且保证 \(h_i\) 互不相同。
对于 \(10\%\) 的数据,\(n \leq 3\);
对于 \(20\%\) 的数据,\(n \leq 10\);
对于 \(50\%\) 的数据,\(n \leq 20\);
对于 \(80\%\) 的数据,\(n \leq 50\);
对于 \(100\%\) 的数据,\(n \leq 300\)。
Code
#include<bits/stdc++.h>
using namespace std;
const int U=3e2+5;
int n,h[U];
long long sum=0;
int main()
{
#ifndef ONLINE_JUDGE
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
cin>>n;
for(int i=1;i<=n;i++) cin>>h[i];
sort(h+1,h+n+1);
for(int i=0,j=n;i<=j;)
{
sum+=(h[i]-h[j])*(h[i]-h[j]);
i++;
sum+=(h[i]-h[j])*(h[i]-h[j]);
j--;
}
if(n%2==0) sum-=(h[n/2]-h[n/2+1])*(h[n/2]-h[n/2+1]);
cout<<sum<<endl;
return 0;
}
// 编译常用指令>> g++ -std=c++14 -O2 -Wall code.cpp -o code
/*
1 2
*/

浙公网安备 33010602011771号