算法设计与分析---greedy算法

算法设计与分析—greedy算法(持续更新中…)

greedy算法概念:
先不管整体的最优解,每一步都是先找当前最优的解,我只管当前最优,第一步最优,第二步最优,…最后到整体也是最优的!当然这种算法也有其局限性,对于一些问题每一步最优并不一定到整体就最优。

不能用贪心解决的例子
在这里插入图片描述
解的步骤:
在这里插入图片描述

问题一,活动安排问题:

问题描述:
n个活动各有一个起始时间s和终止时间f,一个活动正在进行时,在[s,f)这一时间段内其他活动不能开展,求解最优安排活动方案,使时间利用率最高,即安排活动总时间/(max_f-min_s);
input:
输入n为活动数目,接下来的n行每行输入
活动的起始时间 活动的终止时间
求最优的安排方案是用到的时间最大化
output:
输出
最优安排方案所占时间/总时间
以及活动安排的方案(输出n个0或1,1表示安排,0表示未安排);
input_case:

11
1 4
3 5
0 6
5 7
3 8
5 9
6 10
8 11
8 12
2 13
12 14

output_case:

10/14
1 0 0 1 0 0 0 1 0 0 1

思路:
要先按f从小到大排好,先把f最小的活动安排上,计now=f,之后找s_i>=now,如果s_i>=now,
就安排上该活动,并将now赋值为i,依次找到最后问题边求解;
code:

#include<stdio.h>
#include<algorithm>

using namespace std;
 
typedef struct Node{
    
    
	int s;
	int f;
	int isok;
}Act;

bool cmp(Act a,Act b){
    
    
	return a.f<b.f;
}

void f(Act *a,int n){
    
    
	a[0].isok=1;
	int j=0;
	
	for(int i=1;i<n;i++){
    
    
		if(a[i].s>=a[j].f){
    
    
			a[i].isok=1;
			j=i;
		}else{
    
    
			a[i].isok=0;
		}
	}
	return ;
}

int main(){
    
    
	int n;
	scanf("%d",&n);
	Act a[n];
	for(int i=0;i<n;i++){
    
    
		scanf("%d %d",&a[i].s,&a[i].f);
		a[i].isok=0;
	}
	
	sort(a,a+n,cmp);//需要对结构体数组排序,按f从小到大的顺序排序
	f(a,n);
	int sum=0;
	int min_s=a[0].s;
	int max_f=a[0].f;
	for(int i=0;i<n;i++){
    
    
		if(a[i].isok==1){
    
    
			sum+=a[i].f-a[i].s;
		}
		if(a[i].s<min_s){
    
    
			min_s=a[i].s;
		}
		if(max_f<a[i].f){
    
    
			max_f=a[i].f;
		}
	}
	
	printf("%d/%d\n",sum,max_f-min_s);
	for(int i=0;i<n;i++){
    
    
		printf("%d",a[i].isok);
		if(i<n-1)printf(" ");
	}
	
	return 0;
}

问题二,最优装载问题

问题描述:
n个集装箱装在载重为c的船上,1为装上,0为未装上,求最多可以装几个箱子(不考虑空间问题)。
每个箱子有各自的重量,编号;
input:
输入一个正整数n和一个正整数c,分别表示装载箱的个数与船的载重;
接下来的一行输入n个正整数代表n个集装箱的重量;
ouput:
输出最优装载度:
装载的数目/箱子总数
输出各个箱子的装载情况:
(按箱子编号1—n,输出一串0或1,用空格隔开,1为装入,0为未装入)
input_case:

5 6
1 4 3 2 6

output_case:

最大装载度:
3/5
编号1-n的装载情况:
1 0 1 1 0

思路:
按weight从小到大排好,依次装入,直到不能再装为止,就是说按照轻者优先的原则,可以使数量最大化。
code:

#include<stdio.h>
#include<algorithm>

using namespace std;

typedef struct elem{
    
    
	int x;     //编号 
	int weight;//重量 
	int isok;  //是否被装入,0为不装入,1为转入; 
}elem;

bool cmp_weight(elem e1,elem e2){
    
    
	return e1.weight<e2.weight;
}

bool cmp_x(elem e1,elem e2){
    
    
	return e1.x<e2.x;
}

int main(){
    
    
	int n,c;
	scanf("%d %d",&n,&c);
	elem a[n];	
	for(int i=0;i<n;i++){
    
    
		scanf("%d",&a[i].weight);
		a[i].isok=0;
		a[i].x=i+1;
	}
	sort(a,a+n,cmp_weight);//按重量从小到大排好
	
	int sum=0;
	int i;
	for(i=0;i<n&&sum<=c;i++){
    
    
		sum+=a[i].weight;//轻者优先
		a[i].isok=1;
	}
	if(sum>c){
    
    //退一步,以满足题意
		a[i-1].isok=0;
		sum-=a[i-1].weight;
	}
	int sum_isok=0;
	for(int i=0;i<n;i++){
    
    
		if(a[i].isok==1){
    
    
			sum_isok++;
		}
	}
	
	sort(a,a+n,cmp_x);//最后再按编号从小到大排好
	printf("最大装载度:\n%d/%d\n",sum_isok,n);
	printf("编号1-n的装载情况:\n");
	for(int i=0;i<n;i++){
    
    
		printf("%d",a[i].isok);
		if(i<n-1)printf(" ");
	}
	return 0;
}

问题三,单源最短路问题(dijkstra算法)

问题描述:
对于带权有向图G(V,E)而言,求a点到b点的最短路长度,如果走不到就输出-1;

input:
输入四个正整数:
n m aa bb
分别代表结点个数,边的个数,起点,终点;
接下来m行输入每行输入:ii jj len
为结点ii到结点jj的距离为len;
output:
如果aa到bb没有路可走,输出:no way!\n
如果有最短路,输出最短路长度:minlen way:%d\n
input_case:

7 10 1 7
1 2 1
1 3 3
2 3 4
2 5 2
3 4 6
3 6 2
4 6 5
4 5 1
5 7 4
6 7 4

output_case:

minlen way:7

code:

#include<stdio.h>
#define   SIZE   99
#define   INF    9999999
int M[SIZE][SIZE];

int main(){
    
    
	
	int n,m;
	int aa,bb;
	scanf("%d %d",&n,&m);
	scanf("%d %d",&aa,&bb);
	
	for(int i=1;i<=n;i++){
    
    
		for(int j=1;j<=n;j++){
    
    
			M[i][j]=INF;
		}
	}
	
	int ii,jj,len;
	for(int i=0;i<m;i++){
    
    
		scanf("%d %d %d",&ii,&jj,&len);
		M[ii][jj]=len;
	}
	
	int sum=0;
	int isput[n+1]={
    
    0};
	int prim[n+1]={
    
    0};
	for(int i=1;i<=n;i++){
    
    
		prim[i]=M[aa][i];
	}
	
	prim[aa]=0;
	isput[aa]=1;
	prim[0]=INF;
	int flag=0;
	
	while(1){
    
    
		int j=0;
		for(int i=1;i<=n;i++){
    
    
			if(prim[i]<prim[j]&&isput[i]==0){
    
    
				j=i;
			}
		}
		
		if(j==0){
    
    
			flag=0;
			break;
		}
		isput[j]=1;
		if(j==bb){
    
    
			flag=1;
			break;
		}
		
		for(int i=1;i<=n;i++){
    
    
			if(isput[i]==0&&M[j][i]+prim[j]<prim[i]){
    
    
				prim[i]=M[j][i]+prim[j];
			}
		}
	}
	
	if(flag==0){
    
    
		printf("no way!\n");
	}else{
    
    
		printf("minlen way:%d\n",prim[bb]);
	}
	
	return 0;
}

问题四,最小生成树问题(prim算法)

问题引入:
有n个结点,如何建路,使各个结点之间连通(形成连通图),并且使路花费最小;
这里为了简化问题只讨论无向图!
input:
输入正整数n,m
分别代表结点数目,路的个数
接下来m行每行输入aa,bb,len
即分别为起点,终点,路长度;
output:
输出建路的最小花费(见样例)
如果无法连通输出no way!
input_case:

6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3

output_case:

min pay:12

code:

//prim算法 
#include<stdio.h>

#define   INF    99999
#define   SIZE   99

int M[SIZE][SIZE];

int main(){
    
    
	
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
    
    
		for(int j=1;j<=n;j++){
    
    
			M[i][j]=INF;
		}
	}
	
	int aa,bb,len;
	
	for(int i=0;i<m;i++){
    
    
		scanf("%d %d %d",&aa,&bb,&len);
		M[aa][bb]=len;
		M[bb][aa]=len;
	}
	
	int dist[n+1]={
    
    0};
	int put[n+1]={
    
    0};
	for(int i=1;i<=n;i++){
    
    
		dist[i]=M[1][i];
	}
	dist[1]=0;
	put[1]=1;
	dist[0]=INF;
	int sum=0;
	while(1){
    
    
		int j=0;
		for(int i=1;i<=n;i++){
    
    
			if(dist[i]<dist[j]&&put[i]==0){
    
    
				j=i;
			}
		}
		
		if(j==0)break;
		
		sum+=dist[j];
		put[j]=1;
		
		for(int i=1;i<=n;i++){
    
    
			if(dist[i]>M[i][j]){
    
    
				dist[i]=M[i][j];
			}
		}
	}
	int flag=0;
	for(int i=1;i<=n;i++){
    
    
		if(put[i]==0){
    
    
			flag=1;
			break;
		}
	}
	if(flag==1){
    
    
		printf("no way!\n");
	}else{
    
    
		printf("min pay:%d\n",sum);
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/timelessx_x/article/details/115053106