贪心是局部最优一定整体最优。动规是局部最优整体不一定最优
目录
例题一(背包问题)
例题二(活动安排问题)
例题三(最优装载问题)
例题四(多机调度问题)
例题五(采集果子)
例题六(合并果子)
例题七(纪念品分组)
例题八(混合牛奶)
例题九(分发糖果)
【题目描述】
现在有很多物品(物品数量少于100个,它们是可以分割的),已知每个物品的单位重量的价值v和重量w(1<=v,w<=10);如果给你一个背包它能容纳的重量为m(10<=m<=20),你所要做的就是把物品装到背包里,使背包里的物品的价值总和最大。
【输入说明】
每组测试数据的第一行有两个正整数s,m(1<=s<=10);s表示有s个物品。接下来的s行每行有两个正整数v,w。
【输出说明】
输出每组测试数据中背包内的物品的价值和
【输入样例】
3 15
5 10
2 8
3 9
【输出样例】
65
代码:
#include
#include
struct Goods{
int v,w;
}g[100];
int main(){
int s,m;
scanf("%d%d",&s,&m);//s个背包,承重m
for(int i=0 ;i=m)
{
ans +=m*g[i].v;
m = 0;
break;
}
else{
ans += g[i].v*g[i].w;//单价*重量
m = m-g[i].w;//表示剩余背包的重量
}
}
printf("%d\n",ans);
}
设有n个活动的集合E= {1,2,3,4…n },其中每个活动都要求使用同一资源(如演讲会场),而在同一时间内只有一个活动能使用这一资源。每个活动 i 都有一个要求使用该资源的起始时间 Si 和一个结束时间 fi ,且 si < fi ,如果选择了活动,则他在半开时间区间[ si, fi ]内占用资源。若区间[ si,fi ) 与区间[ si,fi )不相交,则称活动 i 与活动 j 是相容的。也就是说,当 si >= fj 或者sj >= fi 时,活动 i 与活动 j 相容。活动安排问题要求在所给的活动集合中选出最大的相容活动子集。
贪心策略:
就是按照每个任务的结束时间进行排序,然后每次取符合条件的最早结束的任务就可以了。
代码:
#include
#define N 15
void sort(int S[],int F[],int n);
void arrange(int S[],int F[],int n);
int A[N]={0}; // 用来存储结果
int t[N]={0}; // 用来存储下标
void sort(int S[],int F[],int n){
int i,j;
//对结束时间进行排序
for(j=1;j<=n;j++) { //有点类似冒泡排序
for(i=1;iF[t[i+1]]){
int temp = t[i];
t[i] = t[i+1];
t[i+1] = temp;
}
}
}
}
void arrange(int S[],int F[],int n){
int i,j=1;
int a,b;
A[t[1]] = 1; //t[1]是结束时间最早的,会被选择
a = t[j]; //a的作用是暂存,t数组中存放的是排序后的下标
for(i=2;i<=n;i++){
//如果结束时间大于第t[i]个的开始时间
if(F[a]>S[t[i]]){
A[t[i]] = 0;
}else{
A[t[i]] = 1;
a = t[i]; //这里如果是t[j],那么赋值后t数组会变乱,因此用a暂存
}
}
}
int main(void){
int S[N]={0},F[N]={0};
int n,i;
printf("请输入活动的个数:");
scanf("%d",&n);
for(i=1;i<=n;i++){
t[i] = i; //t[i]初始化,没排序之前
printf("请输入第%d个活动的开始时间和结束时间:",i);
scanf("%d %d",&S[i],&F[i]);
}
sort(S,F,n);
arrange(S,F,n);
for(i=1;i<=n;i++) {
if(A[i]==1) printf("%d ",i); //输出可以使用资源的活动
}
// printf("\n");
// for(i=1;i<=n;i++){
// printf("%d ",t[i]); //排序后的结果
// }
// printf("\n");
return 0;
}
问题描述:
有n个物体,第i个物体的重量为wi(wi为正整数),选择尽量多的物体,使得总重量不超过C。
贪心策略:
每次选择重量最小的物品装入即可,先对物品按照重量从小到大进行排序,然后依次装入即可。
代码:
#include
#define a 1005
int n,C; //n个物体,最大载重量为C
int we[a]; //第i个物体的重量
int main(){
int i;
int num=0;
int sum=0;
printf("请分别输入物体的数量和装载总重量:");
scanf("%d %d",&n,&C);
for(i=0;i we[k+1]) {
int temp = we[k];
we[k] = we[k + 1];
we[k+1] = temp;
}
}
}
for(i=0;i
【问题描述】:
要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。约定,每个作业均可在任何一台机器上加工处理,但未完工前不允许中断处理。作业不能拆分成更小的子作业。
【算法描述】:
采用最长处理时间作业优先的贪心选择策略可以设计出解多
机调度问题的较好的近似算法。按此策略:
当n<=m时,只要将机器i的[0, ti]时间区间分配给作业i即可,算法只需要O(1)时间。
当n>m时,首先将n个作业依其所需的处理时间从大到小排序。然后依此顺序将
作业分配给空闲的处理机。算法所需的计算时间为O(nlogn)。
#include
#define N 8 //作业数
#define M 5 //机器数
int s[M] = {0,0,0};//每台机器当前已分配的作业总耗时,这里也要做手动输入,根据题目来
//求出目前处理作业的时间和 最小的机器号
int min(int m){
int min = 0;
int i;
for(i=1;i s[i]){
min = i;
}
}
return min;
}
//求最终结果(最长处理时间)
int max(int s[],int num){
int max = s[0];
int i;
for(i=1;i= N){
maxtime = setwork1(time,N);
}else{
maxtime = setwork2(time,N);
}
printf("最多耗费时间%d。",maxtime);
}
一道力扣原题,题目链接:lcp 55采集果子
贪心策略:每次采最多的果子就好了,如果够了就不用采。
int getMinimumTime(int* time, int timeSize, int** fruits, int fruitsSize, int* fruitsColSize, int limit){
int maxTime = 0; // 存放结果
for(int i = 0; i < fruitsSize; i++){ // 遍历每次需要采集的果子数组
// 统计多少次能够采集完成当前的果子
int temp = fruits[i][1]/limit + (fruits[i][1]%limit == 0?0:1);
// 用次数乘每次采集的时间,更新结果
maxTime += temp * time[ fruits[i][0]];
}
return maxTime;
}
洛谷原题,题目链接:1090合并果子
贪心策略:每次都选最小的两个堆合并
#include
#include
#define INT_MAX 2147483647
int n,a[11000],ans=0; // 数组存的是每一堆果子的重量
bool vis[11000]={0};
void hebing(){
int min1=INT_MAX,min2=INT_MAX; // 定义两堆最小的果子
int t1,t2;
for (int j=1;j<=n;++j) // 遍历果子,找到第一个最小的
if ((!vis[j])&&(a[j]
洛谷原题,题目链接:1094纪念品分组
贪心策略:先排序,从两边往中间遍历,能两个分一组就两个分一组,不能就一个分一组
#include
int p[30001]; // 存放物品
// 按下标交换
void swap(int x, int y) {
int temp = p[x];
p[x] = p[y];
p[y] = temp;
}
int p1(int ks, int js) {
int max = p[ks];
int i = ks;
int j = js + 1;
while (1) {
while (p[++i] > max && i < js);
while (p[--j] < max);
if (i >= j) break;
swap(i, j);
}
swap(ks, j);
return j;
}
// 快排
void qSort(int ks, int js) {
int r;
if (ks < js) {
r = p1(ks, js);
qSort(ks, r - 1);
qSort(r + 1, js);
}
}
int main() {
int n, w, i, j, sum = 0;
scanf("%d%d", &w, &n);
for (int i = 0; i < n; i++) {
scanf("%d", &p[i]);
if (w - p[i] < 5) { // 根据条件(5≤Pi≤w)做时间优化了, 就是如果太大直接在这里单个分为一组了
i--;
n--;
sum++;
}
}
qSort(0, n - 1); //排序
for (int i = 0; i < n; i++) {
if (p[i] + p[n-1]>w && i != n-1) sum++;
else sum++, n--;
}
printf("%d",sum);
return 0;
}
洛谷原题,题目链接:1208混合牛奶
#include
struct node{ // 结构体存的牛奶信息,方便排序
int a; // 单价
int b; // 总量
}q[5000];
int main() {
int n, weight, ans=0;
scanf("%d %d", &weight, &n); // 输入农民个数n和牛奶总量w
for (int i = 0;i < n; i++) { // 输入农民的牛奶单价和他能卖出的量
scanf("%d %d", &q[i].a, &q[i].b);
}
// 注意这里j是从0开始的,很像但不是冒泡,当然也可以用冒泡来写,都一样
for (int i = 0; i < n; i++) { // 按照牛奶单价从小到大排序
for (int j = 0; j < n; j++) {
if (q[i].a < q[j].a) {
int temp1 = q[i].a;
int temp2 = q[i].b;
q[i].a = q[j].a;
q[j].a = temp1;
q[i].b = q[j].b;
q[j].b = temp2;
}
}
}
//for (int i = 0; i < n; i++)
// printf("%d ",q[i].a);
while (weight) {
for (int i = 0; i < n; i++) { // 依次选择价值小的放入就好了
if (weight - q[i].b >=0 ) {
weight -= q[i].b;
ans += q[i].a * q[i].b;
} else {
ans += q[i].a * weight;
weight=0;
break;
}
}
break;
}
printf("%d",ans);
return 0;
}
力扣原题,题目链接:135. 分发糖果
#define max(a, b) (((a) > (b)) ? (a) : (b))
int *initCandyArr(int size) {
int *candyArr = (int*)malloc(sizeof(int) * size);
int i;
for(i = 0; i < size; ++i)
candyArr[i] = 1;
return candyArr;
}
int candy(int* ratings, int ratingsSize){
// 初始化数组,每个小孩开始至少有一颗糖
int *candyArr = initCandyArr(ratingsSize);
int i;
// 先判断右边是否比左边评分高。若是,右边孩子的糖果为左边孩子+1(candyArr[i] = candyArr[i - 1] + 1)
for(i = 1; i < ratingsSize; ++i) {
if(ratings[i] > ratings[i - 1])
candyArr[i] = candyArr[i - 1] + 1;
}
// 再判断左边评分是否比右边高。
// 若是,左边孩子糖果为右边孩子糖果+1/自己所持糖果最大值。(若糖果已经比右孩子+1多,则不需要更多糖果)
// 举例:ratings为[1, 2, 3, 1]。此时评分为3的孩子在判断右边比左边大后为3,虽然它比最末尾的1(ratings[3])大,但是candyArr[3]为1。所以不必更新candyArr[2]
for(i = ratingsSize - 2; i >= 0; --i) {
if(ratings[i] > ratings[i + 1])
candyArr[i] = max(candyArr[i], candyArr[i + 1] + 1);
}
// 求出糖果之和
int result = 0;
for(i = 0; i < ratingsSize; ++i) {
result += candyArr[i];
}
return result;
}
【问题描述】
给定一个整数数组,表示在同一行的行星。
每一个元素,其绝对值表示行星的大小正负表示行星的移动方向
正表示向右移动,负表示向左移动每一颗行星以相同的速度移动。
碰撞规则∶
1、两个行星碰撞,较小的行星会爆炸。
2、如果大小相同,则两颗都会爆炸。
3、两颗移动方向相同的行星,永远不会发生碰撞。
#include
#include
#include
int* collision(int *data, int dataSize, int* retSize){
int n = 0;
while(1){
int pre = 0;
int next = 1;
while(next < dataSize){
if(data[pre] * data[next] <0 ){//方向相反
if(data[pre] <0 ){
pre = next;
next ++;
continue;
}
if(data[pre]>abs(data[next])){
data[next] = 0;
n++;
}else if(data[pre]=dataSize){
break;
}
}
*retSize = dataSize - n;
int i,k;
int *retArray = (int*)malloc(*retSize * sizeof(int));
for(i=0,k=0; i
例题十一(种花问题)
【问题描述】
一个很长的花坛,
一部分地已经种植了花,另一部分却没有。花不能种植在相邻的地块上
否则它们会争夺水源,两者都会死去。
给你一个整数数组表示花坛
由若干0和1组成
0表示没种植花,1表示种植了花。
给定一个数n
能不能种下n朵花?
#include
#include
bool canPLaceFlowers(int *data, int dataSize, int n){
int i;
if(n == 0){
return true;
}
int count = 0;
while(i0 && data[i-1] ==1){
i+=1;
}else if(i+1
超: