算法设计与分析—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;
}