题单名称
【算法1-5】贪心
P2240 【深基12.例1】部分背包问题
题目描述
阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N ( N ≤ 100 ) N(N \le 100) N(N≤100) 堆金币,第 i i i 堆金币的总重量和总价值分别是 m i , v i ( 1 ≤ m i , v i ≤ 100 ) m_i,v_i(1\le m_i,v_i \le 100) mi,vi(1≤mi,vi≤100)。阿里巴巴有一个承重量为 T ( T ≤ 1000 ) T(T \le 1000) T(T≤1000) 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?
输入格式
第一行两个整数 N , T N,T N,T。
接下来 N N N 行,每行两个整数 m i , v i m_i,v_i mi,vi。
输出格式
一个实数表示答案,输出两位小数
样例 #1
样例输入 #1
4 50
10 60
20 100
30 120
15 45
样例输出 #1
240.00
思路
题目说是背包问题,其实和背包问题关系不大
做法就是贪心
对于每一堆金币创建一个结构体,内部存放着金币的个数和重量
对于金币重量比对结构体数组进行排序
然后从前往后尽可能多的拿取金币即可
代码
// P2240 【深基12.例1】部分背包问题
// 贪心+结构体排序
#include <bits/stdc++.h>
using namespace std;
struct gold
{
int w;
float v;
}gg[110];
bool cmp(gold a, gold b){
//回调函数
return (a.v/a.w) > (b.v/b.w);
}
int main(int argc, char const *argv[])
{
float ret = 0;
float nowt,t;
int n;
cin >> n >> t;
nowt = t;
//读取数据
for(int i=0; i<n; i++){
cin >> gg[i].w >> gg[i].v;
}
sort(gg, gg+n, cmp);
for(int i=0; i<n; i++){
if(gg[i].w <= nowt){
ret += gg[i].v;
nowt -= gg[i].w;
} else if(gg[i].w > nowt){
ret += nowt*gg[i].v/gg[i].w;
break;
}
}
printf("%.2f",ret);
return 0;
}
P1223 排队接水
题目描述
有 n n n 个人在一个水龙头前排队接水,假如每个人接水的时间为 T i T_i Ti,请编程找出这 n n n 个人排队的一种顺序,使得 n n n 个人的平均等待时间最小。
输入格式
第一行为一个整数 n n n。
第二行 n n n 个整数,第 i i i 个整数 T i T_i Ti 表示第 i i i 个人的等待时间 T i T_i Ti。
输出格式
输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。
样例 #1
样例输入 #1
10
56 12 1 99 1000 234 33 55 99 812
样例输出 #1
3 2 7 8 1 4 9 6 10 5
291.90
提示
n ≤ 1000 , t i ≤ 1 0 6 n \leq 1000,t_i \leq 10^6 n≤1000,ti≤106,不保证 t i t_i ti 不重复。
当 t i t_i ti 重复时,按照输入顺序即可(sort 是可以的)
思路
还是结构体+排序,主要是要储存每个人的序号和打水时间以及等待的时间
然后对于打水时间进行排序,打水打的快的人先打
这样会让等待的人数迅速减少,进而减少人群的平均等待时间
代码
// P1223 排队接水
// 贪心,结构体排序
#include <bits/stdc++.h>
using namespace std;
struct p{
int uid;
int t;
int wait;
}pp[1010];
bool cmp(p a, p b){
return a.t < b.t;
}
int main(){
int n;
double avewait = 0;//坑爹啊,这题要用双精度
cin >> n;
// 读取数据
for(int i=0; i<n; i++){
pp[i].uid = i+1;
cin >> pp[i].t;
pp[i].wait = 0;
}
// 对数据依据打水时间排序
sort(pp, pp+n, cmp);
// 计算等待时间
for(int i=0; i<n; i++){
for(int j=0; j<i; j++){
pp[i].wait += pp[j].t;
}
avewait += pp[i].wait;
}
// 计算平均等待时间
avewait = avewait*1.0/n*1.0;
for(int i=0; i<n-1; i++){
cout << pp[i].uid << " ";
}
cout << pp[n-1].uid << endl;
printf("%.2f",avewait);
}
P1803 凌乱的yyy / 线段覆盖
题目背景
快 noip 了,yyy 很紧张!
题目描述
现在各大 oj 上有 n n n 个比赛,每个比赛的开始、结束的时间点是知道的。
yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。
所以,他想知道他最多能参加几个比赛。
由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 2 2 2 个及以上的比赛。
输入格式
第一行是一个整数 n n n ,接下来 n n n 行每行是 2 2 2 个整数 a i , b i a_{i},b_{i} ai,bi ( a i < b i a_{i}<b_{i} ai<bi ),表示比赛开始、结束的时间。
输出格式
一个整数最多参加的比赛数目。
样例 #1
样例输入 #1
3
0 2
2 4
1 3
样例输出 #1
2
提示
对于 20 % 20\% 20% 的数据, n ≤ 10 n \le 10 n≤10。
对于 50 % 50\% 50% 的数据, n ≤ 1 0 3 n \le 10^3 n≤103。
对于 70 % 70\% 70% 的数据, n ≤ 1 0 5 n \le 10^{5} n≤105。
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 6 1\le n \le 10^{6} 1≤n≤106 , 0 ≤ a i < b i ≤ 1 0 6 0 \le a_{i} < b_{i} \le 10^6 0≤ai<bi≤106。
思路
用结构体保存所有线段的起始位置和结束位置
然后以右端点的位置排序所有线段
从前往后不断的排线段就可以了
代码
// P1803 凌乱的yyy / 线段覆盖
#include <bits/stdc++.h>
using namespace std;
struct line{
int left;
int right;
int use;
}l[1000010];
int cnt = 0;
int n;
void find(int head){
for(int i=0; i<n; i++){
if(l[i].left >= head && l[i].use == 0){
l[i].use = 1;
cnt++;
find(l[i].right);
break;//TLE后加的代码,找到的递归情况一定是最佳,无序再找,直接跳出
}
}
return;
}
bool cmp(line a, line b){
return a.right < b.right;
}
int main(int argc, char const *argv[])
{
cin >> n;
for(int i=0; i<n; i++){
cin >> l[i].left >> l[i].right;
l[i].use = 0;
}
sort(l, l+n, cmp);
find(0);
cout << cnt;
return 0;
}
P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n − 1 n-1 n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 1 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 3 3 种果子,数目依次为 1 1 1 , 2 2 2 , 9 9 9 。可以先将 1 1 1 、 2 2 2 堆合并,新堆数目为 3 3 3 ,耗费体力为 3 3 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 12 12 ,耗费体力为 12 12 12 。所以多多总共耗费体力 = 3 + 12 = 15 =3+12=15 =3+12=15 。可以证明 15 15 15 为最小的体力耗费值。
输入格式
共两行。
第一行是一个整数
n
(
1
≤
n
≤
10000
)
n(1\leq n\leq 10000)
n(1≤n≤10000) ,表示果子的种类数。
第二行包含 n n n 个整数,用空格分隔,第 i i i 个整数 a i ( 1 ≤ a i ≤ 20000 ) a_i(1\leq a_i\leq 20000) ai(1≤ai≤20000) 是第 i i i 种果子的数目。
输出格式
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2 31 2^{31} 231 。
样例 #1
样例输入 #1
3
1 2 9
样例输出 #1
15
提示
对于 30 % 30\% 30% 的数据,保证有 n ≤ 1000 n \le 1000 n≤1000:
对于 50 % 50\% 50% 的数据,保证有 n ≤ 5000 n \le 5000 n≤5000;
对于全部的数据,保证有 n ≤ 10000 n \le 10000 n≤10000。
思路
这题的思路很简单,对于排序的数组取最小的两个加和,然后再从加和后的数组中寻找两个最小值继续加和,反复如此。
问题是时间复杂度的限制,原本我是循环加和n-1次,在这个过程中每次都进行依次sort排序,然后超时了
然后采用了STL库的优先队列,二者都基于堆排序,区别在于,sort函数是对一整个数组进行重新排序,而用优先队列就可以只取数2个,加和后再入队一个数,减少了重构的代价。
代码
// P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
// O(n2logn)
#include <bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int>> a;
int main(){
int n;
cin >> n;
for(int i=0; i<n; i++){
int temp;
cin >> temp;
a.push(temp);
}
long long weight = 0;
for(int i=0; i<n-1; i++){
int ta = a.top();
a.pop();
int tb = a.top();
a.pop();
weight += ta + tb;
a.push(ta + tb);
}
cout << weight;
return 0;
}
P3817 小A的糖果
题目描述
小 A 有 n n n 个糖果盒,第 i i i 个盒中有 a i a_i ai 颗糖果。
小 A 每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中糖的个数之和都不大于 x x x,至少得吃掉几颗糖。
输入格式
输入的第一行是两个用空格隔开的整数,代表糖果盒的个数 n n n 和给定的参数 x x x。
第二行有 n n n 个用空格隔开的整数,第 i i i 个整数代表第 i i i 盒糖的糖果个数 a i a_i ai。
输出格式
输出一行一个整数,代表最少要吃掉的糖果的数量。
样例 #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 6 6 颗,第 4 盒吃掉 2 2 2 颗,第 6 盒吃掉 3 3 3 颗。
数据规模与约定
- 对于 30 % 30\% 30% 的数据,保证 n ≤ 20 n \leq 20 n≤20, a i , x ≤ 100 a_i, x \leq 100 ai,x≤100。
- 对于 70 % 70\% 70% 的数据,保证 n ≤ 1 0 3 n \leq 10^3 n≤103, a i , x ≤ 1 0 5 a_i, x \leq 10^5 ai,x≤105。
- 对于 100 % 100\% 100% 的数据,保证 2 ≤ n ≤ 1 0 5 2 \leq n \leq 10^5 2≤n≤105, 0 ≤ a i , x ≤ 1 0 9 0 \leq a_i, x \leq 10^9 0≤ai,x≤109。
思路
两个盒子的和数超过了限制
那么这里如果从前往后减,则优先减去后面盒子的数,如果后面盒子不够减,再减前面盒子的数
因为这样可以尽可能的让减的盒子处于中央位置,减去的糖果数可以影响到前后两个盒子
代码
// P3817 小A的糖果
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(int argc, char const *argv[])
{
ll ret = 0;
int n;
int x;
vector<int> a;
cin >> n >> x;
//读取数据
for(int i=0; i<n; i++){
int temp;
cin >> temp;
a.push_back(temp);
}
for(int i=1; i<n; i++){
if(a[i]+a[i-1] > x){
int dec = a[i] + a[i-1] - x;
//两个盒子的糖果数和超出x
if(a[i] >= dec){
a[i] -= dec;
ret += dec;
} else if(a[i] < dec){
ret += dec;
a[i-1] -= dec - a[i];
a[i] = 0;
}
}
}
cout << ret;
return 0;
}