首页 > 其他分享 >贪心法求解问题

贪心法求解问题

时间:2023-10-25 13:12:00浏览次数:62  
标签:include 求解 int 复杂度 问题 排序 齐威王 田忌 贪心

一、背包问题

1.1 问题描述

  设有编号为1、2、......、n的n个物品,它们的重量分别为w1、w2、......、wn,价值分别为v1、v2、......、vn,其中wi和vi均为正数,有一个背包可以懈怠的最大重量不超过W。求解目标是在不超过背包附中的前提下使背包装入的总价值最大(即效益最大化)。与0/1背包问题的区别是,这里的每个物品可以取一部分装入背包。

1.2 求解思路

  采用贪心法求解。用结构体存储物品的价值、重量和价重比,在输入价值重量后,首先求每个物品的价重比p=v/w,将所有物品按照价重比进行排序,从价重比最大的物品开始遍历,若当前物品的重量小于背包剩余容量wieght,将这个物品全部放入背包中,直到其中一个物品的重量w大于背包剩余容量或者物品放完,若其中一个物品的重量w大于背包剩余容量,就将其一部分装入背包,剩余若还有物品没装,便置为0。

1.3 详细代码

#include<iostream>
#include<algorithm>
using namespace std;
#define MAXV 51

struct NodeType{
	double v;//价值
	double w;//重量
	double p;//价重比 
}; 

int n;		 //物品件数 
double W;//背包限重 
NodeType a[MAXV]={{0}};//所有物品

double sumv=0;		 //当前背包中物品价值
double x[MAXV]={0};//标记每件物品装了多少进背包 

void KNap(){
	double weight=W;//背包剩余重量
	int i;
	for(i=0;i<n;i++){
		if(a[i].w<weight){//从价重比最大的开始,若能直接装入,就直接装入 
			x[i]=1;
			weight-=a[i].w;
			sumv+=a[i].v;
		}
		else if(a[i].w>weight){//直到其中一个不能完全装入,退出循环 
			break;
		}
	} 
	if(weight>0){//还能继续装入 
		x[i]=weight/a[i].w;			//这件物品装入一部分 
		sumv+=x[i]*a[i].v;
	}
}

bool cmp(NodeType p1,NodeType p2){//排序 
	return p1.p>p2.p;
}

void Disp(){				//输出 
	cout<<"W\t"<<"V\t"<<"V/W"<<endl; 
	for(int i=0;i<n;i++){
		cout<<a[i].v<<"\t"<<a[i].w<<"\t"<<a[i].p<<endl;
	}
}
void Init(){
	cout<<"请输入物品件数:"<<endl;
	cin>>n;
	cout<<"请输入背包限重:"<<endl;
	cin>>W;
	cout<<"请输入物品重量和价值:"<<endl;
	double v,w;
	for(int i=0;i<n;i++){
		cout<<"第"<<i+1<<"件物品:";
		cin>>w>>v;
		a[i].v=v;
		a[i].w=w;
		a[i].p=v/w;
	}
	cout<<"排序前:"<<endl;
	Disp(); 
	sort(a,a+n,cmp);		//排序 
	cout<<"排序后:"<<endl;
	Disp();
	KNap();
	cout<<"求解结果:"<<endl;
	cout<<"x:[ ";
	for(int i=0;i<n;i++){
		if(i==n-1) cout<<x[i];
		else cout<<x[i]<<" , ";
	}
	cout<<"]"<<endl;
	cout<<"总价值:"<<sumv<<endl;
}

int main(){
	Init();
	return 0;
}

1.4 复杂度分析

  排序算法时间复杂度O(nlogn),while循环时间复杂度为O(n),所以本算法的时间复杂度为O(nlogn)。

二、最优装载问题

2.1 问题描述

  有n个集装箱要装上一艘载重量为W的轮船,其中集装箱i(1<=i<=n)的重量为wi。不考虑集装箱的体积限制,现要尽可能多的集装箱装上轮船,使他们的重量之和不超过W。

2.2 求解思路

  题说要尽可能多的集装箱装上轮船,这里采用贪心法求解,选出尽可能多的集装箱个数。因此,当重量限制为W时,wi越小可装载的集装箱个数越多,所以采用优先选取重量轻的集装箱装船。对于已有的集装箱重量数组w[n],通过sort排序对重量按照从小到大的顺序进行排序,并设置一个标记数组x[n],标记集装箱是否被装入,0表示未被装入,1表示已被装入,船剩余载重量weight船当前载重maxw。遍历数组w,当w[i]<weight时,将其装入,设x[i]=1,weight-=w[i],maxw+=w[i]。直到集装箱被完全装入或剩余集装箱装不进时,退出循环,求解完毕。maxw即为最后轮船上的总重量。

2.3 详细代码

#include<iostream>
#include<algorithm>
using namespace std;
#define MAXV 51

int w[]={0,5,2,4,3};//每个集装箱的重量
int n=5,W=10;		// 5个集装箱,船载重为W
int maxw=0;			//存放最优解重量
int x[MAXV]={0};	//标记物品是否被存放 

bool cmp(int a,int b){
	return a<b;
}

void solve(){
	int resw=W;
	sort(w+1,w+n+1,cmp);//对重量进行排序
	for(int i=1;i<=n&&w[i]<=resw;i++){
		maxw+=w[i];
		x[i]=1;
		resw-=w[i];
	}
}

int main(){
	solve();
	cout<<"最优方案为:"<<endl; 
	for(int i=1;i<=n;i++){
		if(x[i]==1) cout<<w[i]<<" ";
	}
	cout<<endl;
	cout<<"最优解重量为:"<<maxw<<endl;
	return 0;
}

2.4 时间复杂度分析

  快速排序时间复杂度为O(nlogn),遍历重量时间复杂度为O(n),故本算法时间复杂度为O(nlogn)。

三、田忌赛马问题

3.1 问题描述

  两千多年前的战国时期,齐威王与大将田忌赛马。双方约定每人各出300匹马,并且在上、中、下三个等级中各选一匹进行比赛,由于齐威王每个等级的马都比田忌的马略强,比赛的结果可想而知。现在双方各n匹马,依次派出一匹马进行比赛,每一轮获胜的一方将从输的一方得到200银币,平局则不用出钱,田忌已知所有马的速度值并可以安排出场顺序,问他如何安排比赛获得的银币最多。输入描述:输入包含多个测试用例,每个测试用例的第一行正整数n(n≤1000)马的数量,后两行分别是n个整数,表示田忌和齐威王的马的速度值。输入n=0结束。输出描述:每个测试用例输出一行,表示田忌获得的最多银币数。

3.2 求解思路

  采用贪心算法。令田忌马的速度数组为t[n],左右两侧分别为leftt、rightt,齐威王马的速度数组为q[n],左右两侧分别为leftq,rightq。田忌获得的银币即为monry。田忌赛马问题可以分为以下几种情况:
(1)田忌最快的马比齐威王最快的马快。此时最优解就是两者比赛,田忌赢。此时rightt--,rightq--,money+=200;
(2)田忌最快的马比齐威王最快的马慢。此时最优解是用田忌最慢的马与齐威王最快的马比赛,田忌输。此时leftt++,rightq--,money-=200;
(3)田忌最快的马和齐威王最快的马一样快。此时又分为以下三种情况:
  1、田忌最慢的马比齐威王最慢的马快。此时最优解就是两者比赛,田忌赢。此时leftt++,leftq++,money+=200;
  2、田忌最慢的马比齐威王最慢的马慢。此时最优解就是用田忌最慢的马与齐威王最快的马比赛,田忌输。此时leftt++,rightq--,money-=200;
  3、其他情况。平局。

3.3 详细代码

#include<iostream>
#include<algorithm>
using namespace std;
#define MAXV 1001

int n;			//马总数
int t[MAXV];	//田忌 
int q[MAXV];	//齐威王
int money=0;	//田忌获得的银币总数 

void solve(){
	sort(t,t+n);//将马的速度按从小到大排序 
	sort(q,q+n);
	int leftt=0;
	int leftq=0;
	int rightt=n-1;
	int rightq=n-1;
	while(leftt<=rightt){
		if(t[rightt]>q[rightq]){//田忌最快的马齐威王最快的马快,田忌赢 
			money+=200;
			rightt--;
			rightq--;
		}
		else if(t[rightt]<q[rightq]){//田忌最快的马比齐威王最快的马慢 
			money-=200;
			leftt++;			//最优解为田忌用最慢的马与齐威王比,田忌输 
			rightq--;
		}
		else {					////田忌最快的马与齐威王最快的马速度相同 
			if(t[leftt]>q[leftq]){//田忌最慢的马比齐威王最慢的马快,田忌赢 
				money+=200;
				leftt++;
				leftq++;
			}
			else if(t[leftt]<q[leftq]){//田忌最慢的马比齐威王最慢的马慢 
				money-=200;
				leftt++;		//最优解为田忌最慢的马与齐威王最快的马比,田忌输 
				rightq--;
			}
		}
	}
}

int main(){
	while(true){
		cout<<"请输入马的总数:";
		cin>>n;
		if(n==0) return 0;
		cout<<"请输入田忌各匹马的速度:"<<endl;
		for(int i=0;i<n;i++){
			cin>>t[i];
		}
		cout<<"请输入齐威王各匹马的速度:"<<endl;
		for(int i=0;i<n;i++){
			cin>>q[i];
		}
		solve();
		cout<<"田忌能获得的银币最多为:"<<money;
	}
	return 0;
} 

3.4 时间复杂度分析

  算法主要花费在快速排序,因此时间复杂度为O(nlogn)。

四、多机调度问题

4.1 问题描述

  设有n个独立的作业{1,2,......,n},由m台相同的及其{1,2,......,m}进行加工处理,作业i所需的处理时间为ti(1<=i<=n),每个作业均可在任何一台及其上加工处理,但未完工前不允许中断,任何作业也不能拆分成更小的子作业。多机调度问题要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。

4.2 问题求解

  采用贪心法求解。题说要求在尽可能短的时间内完成,因此分为两种情况,当n<=m时,机器比作业多,可以为每个作业分配一台机器。当n>m时,作业比机器多,此时考虑长作业优先(因为短作业优先时,最开始每个机器上都是短作业,最后分配长作业会发现非常耗时),使用结构体NodeType存储每个作业,包括no(作业编号)、t(时间)、mmo(分配的机器编号)。首先对结构体数组A[n]按照时间从大到小进行排序,先把每个机器上分配一个作业,使用小根堆qu(小根堆会自动排序)来存放结构体,分配下一轮作业时,按照上一轮作业完成时间越小越先出队即e=qu.top(),qu.pop()。出队后将机器分配给下一个作业,并加上时间A[i].t,在将其添加到小根堆中,直到作业分配完毕。

4.3 详细代码

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

int n=7;	//n个作业
int m=3;	//m个机器 
struct NodeType{
	int no;//作业序号
	int t;//作业时长
	int mmo;//机器序号
	bool operator < (const NodeType& s) const{//按t从大到小排序 
		return t>s.t;
	} 
};
NodeType A[]={{1,2},{2,14},{3,4},{4,16},{5,6},{6,5},{7,3}}; 

void solve(){
	NodeType e;
	if(n<=m){
		cout<<"为每一个作业分配一台机器"<<endl;
		return ;
	}
	sort(A,A+n);
	priority_queue<NodeType,vector<NodeType>,less<NodeType> > qu;//小根堆,小的元素在上面(自动排序) 
	for(int i=0;i<m;i++){
		A[i].mmo=i+1;
		cout<<"给机器"<<i+1<<"分配作业"<<A[i].no<<",执行时间为"<<A[i].t<<",占用时间段[0,"<<A[i].t<<"]"<<endl;
		qu.push(A[i]);
	}
	for(int i=m;i<n;i++){
		e=qu.top();
		qu.pop();
		cout<<"给机器"<<e.mmo<<"分配作业"<<A[i].no<<",执行时间为"<<A[i].t<<",占用时间段["<<e.t<<","<<e.t+A[i].t<<"]"<<endl;
		e.t+=A[i].t; 
		qu.push(e);
	}
}

int main(){
	cout<<"多机调度问题:"<<endl;
	solve();
	return 0;
}

4.4 时间复杂度分析

  算法快速排序时间复杂度O(nlogn),两次for循环时间复杂度共O(n),因此本算法时间复杂度为O(nlogn)。

4.5 小根堆(之前对小根堆了解不多下面记录一下堆)

1、大根堆:是完全二叉树,其中根结点大于子节点;小根堆:是完全二叉树,其中根结点小于子节点。
2、c++中,小根堆和大根堆可以使用优先队列实现(priority_queue),优先级高的先出队,自动排序。

#include<queue>
priority_queue<int> qu;//默认是大根堆
priority_queue<int,vector<int>,greater<int> > qu;//小根堆
priority_queue<int,vector<int>,less<int> > qu;//大根堆

在使用结构体时

#include<queue>
struct Node{
  int x,y;
  bool operator < (const Node& a)const{//小的先进堆
    return x<a.x;
  }
};
priority_queue<Node,vector<Node>,less<Node> > qu;//按照重载后的小于规则,从大到小排序,大的优先级高

标签:include,求解,int,复杂度,问题,排序,齐威王,田忌,贪心
From: https://www.cnblogs.com/QwertyWang/p/GreedyAlgorithm_1.html

相关文章

  • jquery把json字符串转化为json对象需要注意的问题(json用单引号或双引号是不同的)
    1.将json字符串转化为json对象方案一:jquery自带的$.parseJSON函数varjsonstr="{\"id\":\"1\",\"name\":\"jack\"}";varobj=$.parseJSON(jsonstr);说明:使用该方法对json字符串的要求比较高,属性名和属性值必须使用双引号,使用单引号或者不是用引号都会出错 方案二:js自带的eval函......
  • 如何解决问题的能力?
     对问题尽量100%理解 -----》把很多大问题分成小问题 ---------》搜索问题去研究----------》做小的dome实现一下         对问题尽量100%理解 -----》把很多大问题分成小问题 ---------》搜索问题去研究----------》做小的dome实现一下 ......
  • vue中如何使用svg,以及碰到的相应问题
    安装cnpminstallsvg-sprite-loader--save-dev创建svg文件夹存放svg图标创建icons文件夹,在icons文件夹下创建svg文件夹存放本地svg图标。vue.config.js中配置svg图片config.module.rule("svg").exclude.add(resolve("src/icons")).end();config.module.rule......
  • 关于 LLM 和知识图谱、图数据库,大家都关注哪些问题呢?
    自LLM系列文章《知识图谱驱动的大语言模型LlamaIndex》、《Text2Cypher:大语言模型驱动的图查询生成》、《GraphRAG:知识图谱结合LLM的检索增强》陆续和大家见面,以及《夜谈LLM》主题直播同大家交流一番LLM和知识图谱、图数据库之后,在上周NebulaGraph的研发人员做客开......
  • docker-compose: command not found问题的两种常用方法
    docker-compose:commandnotfounddocker-compose是什么Compose定位是「定义和运行多个Docker容器的应用(Definingandrunningmulti-containerDockerapplications)」,其前身是开源项目Fig。在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个......
  • 大模型的幻觉问题
    一什么是幻觉问题大模型的幻觉问题是指大模型生成的答案不基于任何事实数据,简单来说就是杜撰、一本正经的胡说八道。幻觉问题也是影响大模型落地的重要原因之一幻觉问题分类1和用户输入冲突的幻觉2和上下文冲突的幻觉3和事实知识冲突的幻觉(目前重点)例如,大模型在生成医疗......
  • CCS问题
      1、解决CCS下注释乱码及“Noruletomaketarget”问题解决CCS下注释乱码及“Noruletomaketarget”问题_ccs注释乱码_比特冬哥的博客-CSDN博客......
  • 关于单独程序可以访问外网 iis和winserver无法访问外网的问题
    在winserver和iis分别部署了一套发送企微的服务,但是报了一个错 通过ping和浏览器确认网没有问题,然后怀疑是不是有权限的问题,因为这两种服务的权限点不一样给Winserver添加管理员权限。解决 在给iis加权限的时候却遇到了问题,给iis加上管理员权限,并没有什么卵用 后......
  • 英国本科留学期间被开除,来吧,跨本申硕解决问题
    英国本科留学期间被开除,来吧,跨本申硕解决问题出勤率太低、GPA太低、考试挂科、补考挂科、论文不过、引用不当、学术不诚信等等因素都是可能导致本科无法顺利毕业的因素。然而,本科无法顺利毕业的留学生回国之后不寻求任何的帮助,将面临的很可能是一无所有。左同学在英国学习生活了近......
  • 减少软件故障、防范黑客,软件质量安全问题不容忽视
    软件质量的重要性毋庸置疑,而对于开发人员来说,软件质量更多反应的是代码的质量。虽然有报告显示代码质量安全的行业现状显示出持续改进的态势。2022年全年,奇安信代码安全实验室对2001个国内企业自主开发的软件项目源代码进行了安全缺陷检测,整体缺陷密度为10.11个/千行,高危缺陷密度为......