最近一直在做贪心类型的题目
先来道简单的,也是非常典型的题:
# 【深基12.例1】部分背包问题
## 题目描述
阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 $N(N \le 100)$ 堆金币,第 $i$ 堆金币的总重量和总价值分别是 $m_i,v_i(1\le m_i,v_i \le 100)$。阿里巴巴有一个承重量为 $T(T \le 1000)$ 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?
## 输入格式
第一行两个整数 $N,T$。
接下来 $N$ 行,每行两个整数 $m_i,v_i$。
## 输出格式
一个实数表示答案,输出两位小数
## 样例 #1
### 样例输入 #1
```
4 50
10 60
20 100
30 120
15 45
```
### 样例输出 #1
```
240.00
```
#include<bits/stdc++.h>
using namespace std;
struct node
{
double m,v,x;
}a[3000];
bool cmp(node q,node w)
{
return q.x>w.x;
}
inline int read(){
char c;
bool flag=false;
while((c=getchar())<'0'||c>'9')
if(c=='-') flag=true;
int res=c-'0';
while((c=getchar())>='0'&&c<='9')
res=(res<<3)+(res<<1)+c-'0';
return flag? -res:res;
}
int main()
{
int n;
double h=0,t,s=0;
n=read();
t=read();
for(int i=0;i<n;i++){
a[i].m=read();
a[i].v=read();
a[i].x=a[i].v/a[i].m;
}
sort(a,a+n,cmp);
for(int i=0;i<n;i++){
if(h+a[i].m<=t){
h+=a[i].m;
s+=a[i].v;
}
else{
s+=a[i].x*(t-h);
break;
}
}
printf("%.2lf\n",s);
return 0;
}
用了个快读,贪婪的先选取性价比高的。
# 凌乱的yyy / 线段覆盖
## 题目背景
快 noip 了,yyy 很紧张!
## 题目描述
现在各大 oj 上有 $n$ 个比赛,每个比赛的开始、结束的时间点是知道的。
yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。
所以,他想知道他最多能参加几个比赛。
由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 $2$ 个及以上的比赛。
## 输入格式
第一行是一个整数 $n$,接下来 $n$ 行每行是 $2$ 个整数 $a_{i},b_{i}\ (a_{i}<b_{i})$,表示比赛开始、结束的时间。
## 输出格式
一个整数最多参加的比赛数目。
## 样例 #1
### 样例输入 #1
```
3
0 2
2 4
1 3
```
### 样例输出 #1
```
2
```
## 提示
- 对于 $20\%$ 的数据,$n \le 10$;
- 对于 $50\%$ 的数据,$n \le 10^3$;
- 对于 $70\%$ 的数据,$n \le 10^{5}$;
- 对于 $100\%$ 的数据,$1\le n \le 10^{6}$,$0 \le a_{i} < b_{i} \le 10^6$。
类比为线段问题:
在一个数轴上有n条线段,现要选取其中k条线段使得这k条线段两两没有重合部分,问最大的k为多少。
最左边的线段放什么最好?
显然放右端点最靠左的线段最好,从左向右放,右端点越小妨碍越少
其他线段放置按右端点排序,贪心放置线段,即能放就放
#include<bits/stdc++.h>
using namespace std;
struct node
{
int start,end;
}a[1200000];
bool cmp(node x,node y)
{
return x.end<y.end;
}
int main()
{
int n,s=0,v=0;
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d%d",&a[i].start,&a[i].end);
sort(a,a+n,cmp);
for(int i=0;i<n;i++){
if(a[i].start>=v){
v=a[i].end;
s++;
}
}
printf("%d\n",s);
return 0;
}
接下来上点有难度的(虽然难度不高)
# [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
## 题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 $n-1$ 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 $1$ ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 $3$ 种果子,数目依次为 $1$ , $2$ , $9$ 。可以先将 $1$ 、 $2$ 堆合并,新堆数目为 $3$ ,耗费体力为 $3$ 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 $12$ ,耗费体力为 $12$ 。所以多多总共耗费体力 $=3+12=15$ 。可以证明 $15$ 为最小的体力耗费值。
## 输入格式
共两行。
第一行是一个整数 $n(1\leq n\leq 10000)$ ,表示果子的种类数。
第二行包含 $n$ 个整数,用空格分隔,第 $i$ 个整数 $a_i(1\leq a_i\leq 20000)$ 是第 $i$ 种果子的数目。
## 输出格式
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 $2^{31}$ 。
## 样例 #1
### 样例输入 #1
```
3
1 2 9
```
### 样例输出 #1
```
15
```
## 提示
对于 $30\%$ 的数据,保证有 $n \le 1000$:
对于 $50\%$ 的数据,保证有 $n \le 5000$;
对于全部的数据,保证有 $n \le 10000$。
做这道题时注意我们要使用优先队列,如果直接sort排序100%会超时。接着就是一道水题了,直接套公式,模板题。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,x,ans=0;
priority_queue<int,vector<int>,greater<int>>q;
cin>>n;
for(int i=0;i<n;i++)cin>>x,q.push(x);
while(q.size()>=2){
int a=q.top();q.pop();
int b=q.top();q.pop();
ans+=a+b;
q.push(a+b);
}
cout<<ans<<endl;
return 0;
}
# [NOIP2018 提高组] 铺设道路
## 题目背景
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$ 。
这题带一点差分思维,需要递归找找规律。规律是啥我就不详细说了,这篇主要是将贪心思想,其实这题我感觉就和贪心没啥关系。
找到规律之后就非常简单啦。(感觉数组都没必要开)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,s=0;
cin>>n;
int a[200000];
a[0]=0;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)if(a[i]>a[i-1])s+=a[i]-a[i-1];
cout<<s<<endl;
return 0;
}