例12-1:(洛谷P2240)部分背包问题:
#include <iostream>
#include <cstdio>
using namespace std;
int m[110], v[110];
double p[110];
double price(int n,int t) {
if (n == 0) {
return 0;
}
if (t - m[n] > 0) {
return price(n - 1, t-m[n]) + v[n];
}
else {
return v[n] * 1.0*t/m[n];
}
}
int main() {
int n, t;
cin >> n >> t;
for(int i=1;i<=n;i++){
cin >> m[i] >> v[i];
p[i] = 1.0*v[i] / m[i];
}
for (int i = 1; i < n; i++) {
for (int j = i + 1; j <= n; j++) {
if (p[i] > p[j]) {
double m0 = p[i];
p[i] = p[j];
p[j] = m0;
double m1 = m[i];
m[i] = m[j];
m[j] = m1;
double m2 = v[i];
v[i] = v[j];
v[j] = m2;
}
}
}
printf("%.2f", price(n, t));
return 0;
}
证明贪心的第一中方法--假设要选择的方案不是贪心算法所要求的方案,只需要证明将需要贪心的方案替换掉要选择方案,结果会更好(至少不会更差)。
使用贪心策略要特别注意其正确性。
例12-2:(洛谷P1223)排队接水
#include <iostream>
#include <cstdio>
using namespace std;
#include <algorithm>
typedef struct person {
int id;
int time;
}Student;
Student student[1100];
int time1[1100];
bool cmp(Student a,Student b) {
return a.time < b.time;
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> student[i].time;
student[i].id = i;
}
sort(student + 1, student + n + 1,cmp);
for (int i = 1; i <= n; i++) {
cout << student[i].id<<' ';
}
cout << endl;
double average=0;
for (int i = 1; i <= n; i++) {
time1[i]=student[i].time;
}
for (int i = 1; i <= n; i++) {
for (int j = i - 1; j > 0; j--) {
time1[i] += student[j].time;
}
time1[i] -=student[i].time;
average += time1[i];
}
average=1.0*average/n;
printf("%.2f", average);
return 0;
}
例12-3:(洛谷P1803)凌乱的yyy:
#include <iostream>
using namespace std;
#include <algorithm>
struct game {
int beginning;
int ending;
}g[1000001];
bool cmp(struct game a,struct game b) {
if (a.ending != b.ending) return a.ending < b.ending;
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> g[i].beginning >> g[i].ending;
}
sort(g + 1, g + n + 1,cmp);
int sum=0;
int flag = 0;
for (int i = 1; i <= n; i++) {
if (flag <= g[i].beginning) {
sum++;
flag = g[i].ending;
}
}
cout << sum;
return 0;
}
证明贪心的另一种方法:数学归纳法,每一步的选择都是到当前为止的最优解,一直到最后一步就成为了全局的最优解。
如果两个时间冲突,
(1)一个比赛被另一个比赛包含,选先结束的那个。
(2)两个比赛相交,选先结束的那个。
应该选择参加最先结束的那场比赛,接下来要选择能够参加的比赛(所有和上一场冲突的比赛都不能够参加了)中,最早结束的比赛,直到没有比赛可以参加为止。
哈夫曼编码:
例12-4:分卷子
例12-5:合并果子:
#include <iostream>
using namespace std;
#include <algorithm>
#include <cstring>
long long a[10010];
long long b[10010];
int main() {
int n;
cin >> n;
memset(a, 127, sizeof(a));
memset(b, 127, sizeof(b));
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a + 1, a + n + 1);
int x, i = 1, j = 1, n1 = 0, sum=0;
for (int w = 1; w < n; w++) {
x = a[i] < b[j] ? a[i++] : b[j++];//取第一个最小值
x+= a[i] < b[j] ? a[i++] : b[j++];//取第二个最小值
b[++n1] = x;
sum += x;
}
cout << sum;
return 0;
}
使用memset初始化int数组:
第二个参数如果是0,数组就会被初始化为0;
如果是127,会初始化为一个很大且很接近int类型上限的正数;
如果是128,会初始化成很小且接近int类型下限的负数;
如果是-1或者255,时,数组会初始化为-1.