贪心算法的五个经典问题分析和实现

1 篇文章 0 订阅
1 篇文章 0 订阅

问题1:活动安排问题

问题描述:
	现在给你一个会场,有许社团需要在这个会场上活动,
	已知各个社团在这个会场上活动的时间(起始时间和终止时间)
	要求出来怎么安排
		能够使得这个教室z在这一天之内接待尽可能多的社团
  •  解题思路与算法思想

     已经知道我们有n个活动需要安排
     	不妨考虑我们需要首先安排哪个活动
              如果这n个活动的开始时间分别为a1-an
     		  结束时间分别为b1-bn
     			那么对于我们要安排的第一个活动而言(假设这个活动是第i个)
     				无论他的开始时间是多少
     				**他占用的时间都是bi**
     这是由于虽然她只占用了ai-bi的时间
     	但是其实第一个活动,那么从0 - ai时间段里注定没有活动
     在选取好了第一个活动之后 		
     	我们需要选取第二个活动 
     	那么通过将和第一次活动重合的全部活动去掉的方式
    

    我们可以将这个问题转化成选取第一个活动的问题

  • 程序模型的建

  • 那么可以通过各个社团的结束时间对各个社团进行排序

  • 使得结束时间最早的以一些社团位于最前面
    -利用贪心的思想

    				每一都选取结束时间最靠前的哪一个
    				同时去除其他与这个被选择社团时间冲突的社团
    				再进行下一次比较
    
  • 数据结构的选用

  •  利用数组去储存各个社团的开始和终止时间
    
  •  程序设计流程

  •  输入数据
    
  •  进行排序
    
  •  得到结果
    
  • 程序设计伪码算法

     get things stored in a 
     sort a
     while(i<a,size()) 
     {
     	if(现在第第一位的开始时间等于大于上一个被选中社团的终止时间)
     	{
     		计数器+1 ;
     	}
     	i++ ;
     }
    

 源程序编码清单

#include<iostream>
#include<utility>
#include<vector>
#include<algorithm>
using namespace std ;

bool comp(pair<int ,int >a ,pair<int ,int>b) ;

int main(void)
{
	vector< pair<int, int> > b ;
	int n  ;
	scanf("%d",&n) ;
	b.resize(n) ; 
	for(int i = 0 ;i<n ;i++)
	{
		int tem  ;
		scanf("%d",&tem) ;
		b[i].first = tem ;
		scanf("%d",&tem) ;
		b[i].second = tem ;
	}
	
	sort(b.begin(),b.end(),comp) ;
	int end ;
	end =b[0].second;
	
	int cnt = 1 ;
	
	for(int i = 0 ;i<b.size() ;i++)
	{
		if(b[i].first >= end)
		{
			end = b[i].second ;
			cnt++ ;
		}
	}
	
	
	printf("%d ",cnt) ;
	
	return 0 ;
	
	
	
	
	
}

bool comp(pair<int ,int >a ,pair<int ,int>b)
{
	if(a.second<b.second)
	{
		return true ;
	}
	else
	{
		return false ;
	}
}

 
  •  程序输入、输出

     输入:
     		5
     		1 2 3 4 5 6 7 8 9 10
     输出:5
    

输入输出文件或程序运行结果截图

  • 时间与空间复杂度分析

     时间复杂度:
     	nlogn+n
    

 程序使用说明
 总结与完善

“0-1 背包问题

  • 问题描述
  •  			有一容积有限的背包(容积为V)
    
  •  			现在有n个物品,每个物品都有自己的价值和提及
    
  •  			如何知道一个较优的策略,使得能够放进背包里价值之和最大的的物品
    

 解题的思路

先把各个物品的价值密度求出来 价值/体积
之后通过贪新的思路优先那密度最高的,直到装不下为止

 程序模型的建立

  • 利用贪新的思路
  • 每次选择密度大的哪一个

 数据结构的选用

  • 利用数组进行存储
  • 之后在数组中i进行排序

 程序设计流程

  • 存入数据
  • 进行密度计算
  • 进行排序
  • 进行贪心

 程序设计伪码算法

get a sorted
	while(容量小于选择所有物品的体积值之和)
		选择价值密度最大的哪一个

 源程序编码清单

#include<iostream>
#include<vector>
#include<utility>
#include<algorithm>
using namespace std ;
bool comp(pair<int ,double> a  , pair<int ,double> b ) ;
int main(void)
{
	int n ;
	scanf("%d",&n) ;
	int content ;
	scanf("%d",&content) ;
	vector< pair<int , double> > a ;
	a.resize(n) ;
	int tem ; 
	for(vector< pair<int ,double> >::iterator it = a.begin() ; it !=a.end() ;it++ )
	{
		scanf("%d",&tem) ;
		(*it).first = tem ;// capibility it cost ;
		scanf("%d",&tem) ;
		(*it).second = tem ;//srcond if value ;
		(*it).second = (*it).second/(*it).first  ;//the ρof things ; 
	}
	sort(a.begin(),a.end(),comp) ;
	int cnt = 0 ;
	for(vector< pair<int ,double> > :: iterator it = a.begin() ;it != a.end() ;it++)
	{
			if(cnt+ (*it).first <=   content)
			{
				printf("%d %f\n", (*it).first ,(*it).second) ;
			}
			cnt+=(*it).first ;
	} 
	
	
}
bool comp(pair<int ,double> a  , pair<int ,double> b ) 
{
	if(a.second > b.second)
	{
		return true ;
		
	}
	else
	{
		return false ;
	}
}

 程序输入、输出

输入
	7 80
	10 7 
	20 8 
	30 9
	40 12
	4 7 
	14 6
	5 7
输出
	4
	5
	10
	14
	20

输入输出文件或程序运行结果截图
在这里插入图片描述
 时间与空间复杂度分析

  • 时间复杂度:n

 程序使用说明
 总结与完善

“多机调度问题

问题描述

给了你n台机器,m个物品需要加工
	每一个物件加工花费的时间是bi ;
我们需要寻找到一个方案,使得这个加工的总时间较短

 解题思路与算法思想

  • 很原始的贪心模型
  • 从最长的时间开始贪心
		由于耗时最长的物件需要连续加工很长时间
		所以最基本的想法就是先让机器加工最长耗时的物件
			为了防止所有其他机器都在等待一个机器的情况

 程序模型的建立

  • 通过对物品的加工好是的排序
  • 之后让已分配加工时间最短的机器加工当时耗时最长的物品

 数据结构的选用

  • 选数组储存时间

 程序设计流程

  • 读入数据
  • 数据排序
  • 物件分配

 程序设计伪码算法

sort(所有需要的时间)
while(所有物品没有别分配完)
{	
	find(现在被分配任务最少的机器)
	把这个机器分配上未加工物件中需要耗时最长的
}	

 源程序编码清单

#include<iostream>
#include<stdio.h>
#include<vector>
#include<utility> 
#include<algorithm> 
using namespace std ;
bool comp(pair<int ,int>a ,pair<int ,int> b) ;
int main(void)
{
	
	int n ;
	scanf("%d",&n ) ;
	int m ;
	scanf("%d",&m) ;
	if(n>=m)
	{
		vector<int>b ;
		int tem_int ;
		for(int i = 0 ;i<m ;i++)
		{
			scanf("%d",&tem_int) ;
			b.push_back(tem_int) ;
		}
		sort(b.begin(),b.end()) ;
		printf("%d",b.back()) ;//can be used like this 
		return 0 ;
	}
	else
	{
		vector<pair<int ,int > > b ;
		int tem_int ;
		pair<int ,int >tem_pair ;
		for(int i = 0 ;i<m ;i++)
		{
			scanf("%d",&tem_int) ;
			tem_pair = make_pair(i,tem_int) ;
			b.push_back(tem_pair) ; 
		}
		
		sort(b.begin(),b.end(),comp) ;
		
		
		vector<int> time ;
		
		for(int i = 0 ; i<n ;i++)
		{
			time.push_back(0) ;
		}
		
		//已经排好序了??????????????????????????????? 
		//怎么贪心??????????????????? 
		int cnt = 0 ;
		for(int i = 0 ;i<m ;i++)
		{
			vector<int>::iterator  it = min_element(time.begin(),time.end()) ; 
			(*it) = (*it)+b[i].second ;
			
		}
		
		printf("%d",*max_element(time.begin(),time.end())) ;
		
		
	}
	
} 
bool comp(pair<int ,int>a ,pair<int ,int> b) 
{
	if(a.second>b.second)
	{
		return true ;
	}
	else
	{
		return false ;
	}
}

 程序输入、输出

输入:
	3 10
	1 2 3 4 5 6 7 8 9 10
输出
	19

输入输出文件或程序运行结果截图
在这里插入图片描述
 时间与空间复杂度分析

  • 时间复杂度:nlogn+n

 程序使用说明
 总结与完善

“01”子序列问题

问题描述

给定 n 个二进制字符串,要求调整字符串顺序并连接所有的字符
串,使最后得到的字符串中有尽可能多的“01”子序列。输出“01”子序
列的最大个数。 (1 ≤ n ≤ 100000)。

 解题思路与算法思想

如果用贪心的思路来解这道题,那么就涉及到一个贪心策略问题
	这里我们不妨把一个子串的含1量作为其唯一的评判标准
	那么其评判标准的计算公式为 这个子串中1的数量/这个子串符号总数

 程序模型的建立

  • 利用贪心模型
  • 每次都选取数量最大的放到最前面

 数据结构的选用

  • 利用数组中储存

 程序设计流程

  • 输入
  • 计算密度
  • 排序
  • 贪心

 程序设计伪码算法

for(all the string)
{
	a,push_back(1的密度) ;
}
sort(a) ;
while(还有字符串没有被选择)
{
		把当面位置的这个字符串放到最后面
		
}

 源程序编码清单

#include<iostream>
#include<stdio.h>
#include<utility>
#include<vector>
#include<algorithm>
using namespace std ;
bool comp(pair<int ,double> a ,pair<int ,double> b) ;
int main(void)
{
	vector<string>a ;
	int n ; 
	scanf("%d",&n) ;
	string tem ;
	for(int i = 0 ;i<n ;i++)
	{
		cin>>tem ;
		a.push_back(tem) ;
	}
	vector<pair<int ,double> > b ;
	for(int i = 0 ;i<n ;i++)
	{
		double sum = 0 ;
		for(int j = 0 ;j<a[i].size() ;j++)
		{
			sum += (a[i][j]-48) ;
		}
		sum = sum/(double)a[i].size() ;
		pair<int ,double >  tempair = make_pair(i,sum) ;
		b.push_back(tempair) ;
	}
	sort(b.begin(),b.end(),comp) ;
	for(int i = 0 ;i<b.size() ;i++)
	{
		cout<<a[b[i].first] ;
	}
	
}

bool comp(pair<int ,double> a ,pair<int ,double> b) 
{
	if(a.second>b.second)
	{
		return true ;
	}
	else
	{
		return false ;
	}
}
 

 程序输入、输出输入

输入:
5
1111
1101
0011
1000
输出:
1111110101100111000

输入输出文件或程序运行结果截图
在这里插入图片描述
 时间与空间复杂度分析

  • 时间复杂度(n*M) ;

 程序使用说明
 总结与完善

均分纸牌问题

问题描述

有 N 堆纸牌,编号分别为 1,2,…,N。每堆上有若干张,
但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,
然后移动。
移牌规则为:在编号为 1 的堆上取的纸牌,
只能移到编号为 2 的堆上;
在编号为 N 的堆上取的纸牌
只能移到编号为 N-1 的堆上;
其他堆上取的纸牌,
可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,
用最少的移动次数使每堆上纸牌数都一样多。 

 解题思路与算法思想

  • 我们遇到的首要问题就是如何解决重复交换问题
  • 解决方案是我们以从左往右或者从右往左的顺序,每一次都将这个排队移动到最终结果一应该成为的状态
不妨设某一个堆的数量为a,而均分之后的数量为b
并且我们从左向右遍历
	如果a=b ,不做操作,次数+0
	如果a>b,那么将a-b张纸牌移动到右边的相邻堆上,次数+a-b
	如果a<b ,那么把b-a个东西从右边相邻的堆上移动过来 次数+b-a

 程序模型的建立

图上一条所描述
要注意的一点为第三种情况下
	如果右边相邻的地方数量<b-a
	那么我们就把右边的个数记为负数就可以了

 数据结构的选用

  • 利用数组储存

 程序设计流程

  • 输入
  • 遍历
  • 遍历的同时移动+计数

 程序设计伪码算法

for(所有的排队)
{
	如果a=b ,不做操作,次数+0
	如果a>b,那么将a-b张纸牌移动到右边的相邻堆上,次数+a-b
	如果a<b ,那么把b-a个东西从右边相邻的堆上移动过来 次数+b-a
}

 源程序编码清单

#include<iostream>
#include<stdio.h>
#include<vector>
using namespace std ;
int main(void)
{
	int n ;
	scanf("%d",&n) ;
	vector<int> a ;
	int tem ; 
	int sum  = 0 ;
	for(int i = 0 ;i<n ; i++)
	{
		scanf("%d",&tem) ;
		a.push_back(tem) ;	
		sum += tem ;
	}
	sum = sum/n ;
	for(int i =  0 ;i<a.size() ;i++)
	{
		a[i]-=sum ;
	}
	int action = 0  ;
	for(int i = 0 ;i<a.size() ;i++)
	{
		if(a[i]<0)
		{
			action++ ;
			a[i+1] += a[i] ;
		}
		if(a[i]>0)
		{
			action ++;
			a[i+1] += a[i] ;	
		}	
	}
	
	printf("%d",action) ;
	 
}

 程序输入、输出

输入:
	5
	1 2 3 4 10
输出
	20

输入输出文件或程序运行结果截图
在这里插入图片描述
 时间与空间复杂度分析
 程序使用说明
 总结与完善

  • 5
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,背包问题是一个经典的动态规划问题,但也可以使用贪心算法来解决。下面是使用贪心算法解决背包问题的JAVA代码: ```java import java.util.Arrays; public class KnapsackProblem { public static void main(String[] args) { int[] weight = {10, 20, 30}; // 物品重量 int[] value = {60, 100, 120}; // 物品价值 int capacity = 50; // 背包容量 double maxValue = getMaxValue(weight, value, capacity); System.out.println("背包能装的最大价值为:" + maxValue); } /** * 获取背包能装的最大价值 * @param weight 物品重量 * @param value 物品价值 * @param capacity 背包容量 * @return 背包能装的最大价值 */ public static double getMaxValue(int[] weight, int[] value, int capacity) { int n = weight.length; double[] unitValue = new double[n]; // 单位价值 for (int i = 0; i < n; i++) { unitValue[i] = (double) value[i] / weight[i]; } int[] index = new int[n]; // 物品索引 for (int i = 0; i < n; i++) { index[i] = i; } Arrays.sort(index, (i, j) -> Double.compare(unitValue[j], unitValue[i])); // 按单位价值从大到小排序 double maxValue = 0; // 背包能装的最大价值 for (int i = 0; i < n && capacity > 0; i++) { int j = index[i]; double w = Math.min(weight[j], capacity); // 能装下的重量 maxValue += w * unitValue[j]; // 能装下的最大价值 capacity -= w; } return maxValue; } } ``` 在这个代码中,我们计算每个物品的单位价值,并按照单位价值从大到小排序,然后依次选择单位价值最大的物品放入背包中,直到背包无法再装下物品为止。 这里使用了lambda表达式来简化排序过程,如果不理解可以使用传统的Comparator或Comparable来进行排序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值