贪心算法,多机调度,哈夫曼,最短路径,打水,分糖果,最小生成树

文章探讨了贪心算法在人民币找零问题中的应用,证明了在特定面值组合下贪心策略的有效性。接着介绍了多机调度问题的解决方案,展示了一个作业分配的实例。然后讲解了哈夫曼编码的构建过程,并计算了带权路径长度。此外,还涉及了最短路径问题的Dijkstra算法,以及两个趣味问题——打水问题和分糖果问题的求解策略。
摘要由CSDN通过智能技术生成

贪心找零:
  • 最新版的人民币面值有:1角,5角,1元,10元,20元,50元,请分析这套面值方案可以使用贪心法找零吗?

可以使用贪心法找零,首先对于1角,5角,1元,10元,20元,5种钞票的找零,由于他们之间存在倍数关系,因此一定能够采用贪心策略。

那么现在,我们是不是可以肯定出,在上述的6张包括50的钞票中,50元以下的面值是一定能够采用贪心的(用前面我们提到的5张)。

那么现在,对于50元,我们发现也是能够采用贪心策略。而同期不包括50的贪心却需要(20,20,10),因此我们可以发现50的贪心策略可行。

那我们是不是能够确认50内的贪心优化是最好的。

那么我们接下来考虑60范围内(包括60)的数据,我们采取的考虑一定是50+剩余不足(10元)的部分,因此确定能够采用贪心策略。

接下来考率70范围内,考虑50+10+剩余(pass20+20+20+剩余);因此70以内也可以

接下来考虑80范围内,考虑50+20+剩余;显然无误;也可以贪心找零。

接下来确定90,100,同理考虑仍然可以。

对于100以上部分,我们可知一定会采用两张五十替代,剩余部分与上述相同。

对于贪心和动态规划来说,没有十分明确的对症,实际题目的判断应该是在无法 明确倍数关系时,能够确定一定能够使用,否则尽量采取动态策略。

多机调度问题:
  • 问题描述
    设有n个独立的作业{1, 2, …, n},由m台相同的机器{M1, M2, …, Mm}进行加工处理,作业i所需的处理时间为ti(1≤i≤n),每个作业均可在任何一台机器上加工处理,但不可间断、拆分。多机调度问题要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。
  • 设7个独立作业{1, 2, 3, 4, 5, 6, 7}由3台机器{M1, M2, M3}加工处理,各作业所需的处理时间分别为{2, 14, 4, 16, 6, 5, 3}
#include<iostream>
#include<algorithm>
using namespace std;

#define N 10000
int n,m;
int machineWork[N][N];
int workTime[N];
int rear[N];
//多机调度问题解决
/**
问题描述
  设有n个独立的作业{1, 2, …, n},由m台相同的机器{M1, M2, …, Mm}进行加工处理,作业i所需的处理时间为ti(1≤i≤n),每个作业均可在任何一台机器上加工处理,但不可间断、拆分。多机调度问题要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。
  设7个独立作业{1, 2, 3, 4, 5, 6, 7}由3台机器{M1, M2, M3}加工处理,各作业所需的处理时间分别为{2, 14, 4, 16, 6, 5, 3}。
**/


struct Work{
	int number;
	int time;
}work[N];

bool cmp(Work a,Work b){
	if(a.time==b.time)
		return a.number>b.number;
	return a.time>b.time;
}

void assign_work(){
	// 先给所有机器分配任务 
	fill(rear,rear+m,0);
	for(int i=0;i<m;i++){
		workTime[i]=work[i].time;
		machineWork[i][0]=work[i].number;
	
	}
	//给剩下的作业分配最空闲机器
	for(int i=m;i<n;i++){
		//循环判断哪个机器最闲 
		int k=0;
		for(int j=1;j<m;j++){
			if(workTime[j]<workTime[k]){
				k=j;
			}
		}
		rear[k]=rear[k]+1;
		machineWork[k][rear[k]]=work[i].number;
		workTime[k]+=work[i].time;
	} 
}

void output(){
	//输出
	cout<<"各机器工作的作业情况为" ;
	for(int i=0;i<m;i++){
		cout<<"第"<<i+1<<"台机器作业为:";
		for(int j=0;j<=rear[i];j++){
			cout<<"作业"<<machineWork[i][j]<<" ";
		} 
		cout<<"工作总时长为"<<workTime[i]<<endl;
	}
}
void input(){
	cin>>n>>m;
	
	for(int i=0;i<n;i++){
		cin>>work[i].time;
		work[i].number=i+1;
	}
	//根据 
	sort(work,work+n,cmp);
	//打印 
	/**
	for(int i=0;i<n;i++){
		cout<<work[i].time<<" ";
	}
	**/
	assign_work();
	output();
	
}

int main(){
	input();
	return 0;
} 
哈夫曼编码:
#include<iostream>
#include<algorithm>
#include<string>

using namespace std;
#define N 10000
int n;
string path[N];
struct Node{
	int number;
	int W;
	int Lchild;
	int Rchild;
	int Father;
	string Rec;
	int L;
}node[N];

bool cmp_w(Node a,Node b){
	return a.W<b.W;
}

bool cmp_n(Node a,Node b){
	return a.number<b.number;
}

void input(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>> node[i].W;
		node[i].number=i;
	}
}

void createNode(int k){
	//初始化创建节点的条件
	int t=k+n;
	node[t].number=t;
	int l,r;
	l=2*k-1,r=2*k;
	//父节点初始 
	node[t].number=t;
	node[t].W=node[l].W+node[r].W;
	node[t].Lchild=node[l].number;
	node[t].Rchild=node[r].number;
	//孩子初始
	node[l].Father=node[t].number;
	node[r].Father=node[t].number;
	node[l].Rec="0";node[r].Rec="1";
	sort(node+1,node+n+k+1,cmp_w);
}

void createTree(){
	//创建n-1个非叶子节点
	for(int i=1;i<=n-1;i++){
		createNode(i);
	} 
}
/**
void output(){
	for(int i=1;i<=2*n-1;i++){
		cout<<node[i].W;
	} 
}
**/
void Huffman(){
	sort(node+1,node+2*n,cmp_n);
	for(int i=1;i<=n;i++){		//对所有叶子节点 
		path[i]="";
		int k=i;
		node[i].L=0;
		while(node[k].Father!=0){
			path[i]=node[k].Rec+path[i]+"";	//路径放在前面 
			node[i].L++;
			k=node[k].Father;
		}
	}
	//带权路径长度是 
	int totalweight=0;
	for(int i=1;i<=n;i++){
		totalweight+=node[i].W*node[i].L;
		cout<<"节点"<<i<<"的编码为"<<path[i]<<endl;
	} 
	cout<<"带权路径长度为:"<<totalweight<<endl; 
}

int main(){
	input();
	sort(node+1,node+n+1,cmp_w);
	createTree();
	Huffman();
	//output(); 
	
}

/**
输入:
最短路径问题(Dijkstra)
#include<iostream>
#include<algorithm>
#include<queue>
#define N 1000
const int INF=100000;
using namespace std;

/**测试用例
5 6 0
0 1 10
0 2 3
1 2 1
1 3 2
2 1 4
2 3 8
**/
/**
结果
0
7
3
9
INF
**/


int main(){
	int n,m,s;
	cin>>n>>m>>s;
	//输入边点间距
	vector<vector<pair<int,int>>> graph(n); 
	for(int i=0;i<m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		graph[u].push_back(make_pair(v,w));
	} 
	
	//建立优先队列,用来保存最短路径和点
	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq;
	//建立距离表
	vector<int> dist(n,INF);
	dist[s]=0;
	//将起始点加入优先队列
	pq.push(make_pair(0,s)); 
	
	while(!pq.empty()) {
		//取节点
		int u=pq.top().second; 
		pq.pop();
		
		//遍历该节点的相邻节点 
		for(auto next:graph[u]){
			int v=next.first;
			int w=next.second;
			//如果这个节点到原点距离更短,能够刷新 
			if(dist[v]>dist[u]+w){
				dist[v]=dist[u]+w;
				//节点入队
				pq.push(make_pair(dist[v],v)); 
			}
		} 
	}
	
	//输出
	for(int i=0;i<n;i++){
		cout<<dist[i]<<" ";
	} 
	return 0;
}
打水问题
  • 有n个人排队到r个水龙头去打水,他们装满水桶的时间t1,t2,```,tn为整数且各自不相等,应如何安排打水顺序才能使他们总共花费时间最少
#include<iostream>
#include<algorithm>
#include<vector> 
using namespace std;


int main(){
	int n,m;
	cin>>n>>m;
	int time_spend[n];
	for(int i=0;i<n;i++){
		cin>>time_spend[i];
	}
	//进行排序
	sort(time_spend,time_spend+n);
	//进行装水
	int water_work[m][n];
	int total_time=0;
	//先前两个人装水 
	int i;
	vector<int> time_work(m,0);	//记录每列的总时间 
	for(i=0;i<m;i++){
		water_work[i][0]=time_spend[i];
		time_work[i]+=water_work[i][0];
		total_time+=time_spend[i];
	}
	//定义指向水龙头的数组
	vector<int>  pointer(m,0);
	
	while(i<n){
		int point=1;	//查找当下时间最短的 
		int min=0;
		while(point<m){
			if(time_work[point]<time_work[min]){
				min=point;
			}
			point++;
		}
		water_work[min][++pointer[min]]=water_work[min][pointer[min]-1]+time_spend[i++];
		total_time+=water_work[min][pointer[min]];
	}
	cout<<total_time;
}
分糖果问题

一群孩子做游戏,现在请你根据游戏得分来发糖果,要求如下:

  1. 每个孩子不管得分多少,起码分到一个糖果。

  2. 任意两个相邻的孩子之间,得分较多的孩子必须拿多一些糖果。(若相同则无此限制)

给定一个数组 arrarr 代表得分数组,请返回最少需要多少糖果。

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	int n;
	cin>>n;
	int score[n+1];
	for(int i=1;i<=n;i++){
		cin>>score[i];
	}
	int candy[n+1];
	fill(candy,candy+n,1);
	int totalcandy=n;
	int point=0;
	while(point<n){
		if(score[point]<score[point+1]){
			candy[point+1]++;
			totalcandy++;
		} 
		point++;
	}
	while(point>=1){
		if(score[point]>score[point+1]){
			candy[point]++;
			totalcandy++;
		}
		point--;
	}
	cout<<totalcandy<<endl;
}
/**
9
8 9 2 3 4 7 3 1 8
输入1
3
1 0 2
5
输入2 
3
1 2 2
4
**/
最小生成树
#include<iostream>
using namespace std;
#include<vector>
#define N 100
#define INF 100000

int n,m,s;
int visited[N];
int dist[N];
int sum;
int graph[N][N];

/**
 * 假设S是顶点的全集,V是已经访问的节点,S-V代表没有访问的节点,用dist[i]表示到V-(S-V)某顶点的最短距离
 */

int prim(int s){
	dist[s]=0;
	for(int i=1;i<=n;i++){
		//TODO
		int cur=-1;
        //添加下一下已经访问节点
		for(int j=1;j<=n;j++){
			//TODO
			if(!visited[j]&&(cur==-1||dist[j]<dist[cur])){
				cur=j;
			}
		}
		//
		if(dist[cur]==INF) 
			return INF;
		visited[cur]=1;
		sum+=dist[cur];
		//对增添的点进行重新的dist赋值
		for(int k=1;k<=n;k++){
			if(!visited[k]&&graph[cur][k]<dist[k]){
				dist[k]=graph[cur][k];
			}
		}

	}
	return sum;
}

int main(){
/**
 * n:点的个数
 * m:边的条数
 * s:起始点
 */
	cin>>n>>m;
	//输入图
	fill(graph[1],graph[1]+N*N,INF);
	fill(dist,dist+N,INF);
//	for(int i=1;i<=n;i++){
//		//TODO
//		cout<<dist[i];
//	}
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		graph[u][v]=w;
		graph[v][u]=w;
	}
//	visited[0]=1;
	//遍历寻找下一个点
	
	int value = prim(1);
	if(value >= INF) puts("impossible");
	else printf("%d\n",sum);
	return 0;
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值