贪心算法的几个应用

来源:互联网 发布:wenger 知乎 编辑:程序博客网 时间:2024/05/16 07:14
贪心算法具有2个性质:

1、贪心选择性质:只在当前状态下做最优选择,即局部最优选择,再自顶向下,去解做出这个选择后产生的相应子问题。每做一次选择,问题就转化为规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步做出的选择最终导致问题的整体最优解。

2、最优子结构性质:问题的最优解包含子问题的最优解。

 

贪心算法的几个应用:

哈夫曼编码:二叉树&最小优先级队列

dijktra:原始集合、选中节点集合、dist

最小生成树:prim:原始集合、选中几点集合、closestlowcost

最小生成树:kruskul:并查集&最小优先级队列

 

具体代码: 

dijktra:

设邻接矩阵:a[][],有n个节点。

1.初始化:dist[i] = a[v][i],原始集合中只有v

2.dist的最小值,加入原始集合中,更新其他dist,更新n-1

具体代码如下:

/*

5

0 10 10000 30 100

10000 0 50 10000 10000

10000 10000 0 10000 10

10000 10000 20 0 60

10000 10000 10000 10000 0

 

1

*/

 

#include<stdio.h>

#include<stdlib.h>

 

#define NUM 10

#define MAX_VALUE 10000

 

int dist[NUM];

int s[NUM];

int pre[NUM];

 

void  dijkstra(int v,int a[NUM][NUM], int n){

      //初始化

      int i=1,j=1;

      for(i=1; i<=n; i++){

           dist[i] = a[v][i];

           s[i] = false;

           if(a[v][i] == MAX_VALUE){

                 pre[i] = 0;

           }else{

                 pre[i] = v;

           }

      }    

     

      s[v] = true;

      dist[v] = 0;

     

      //更新n-1次

      int min = 0,tmp=v;

      for(i=1; i<n; i++){

            //找最小的dist

            min = MAX_VALUE;

            for(j=1; j<=n; j++){

                     if(!s[j] && min>dist[j]){

                              min = dist[j];

                              tmp = j;

                     }

            }

            s[tmp] = true;   //tmp的初始值是v,不能为0或者随意

     

            //用最小的dist更新已有的dist

            for(j=1; j<=n; j++){

                     if(!s[j] && a[tmp][j] < MAX_VALUE && (dist[tmp]+a[tmp][j] < dist[j])){

                              dist[j] = dist[tmp]+a[tmp][j];

                              pre[j] = tmp;

                     }

            }

      }

}

 

int main(){

    int i=1,j=1,v,n,a[NUM][NUM];

   

    //输入邻接矩阵

    scanf("%d",&n);

    for(i=1; i<=n; i++){

       for(j=1; j<=n; j++){

           scanf("%d",&a[i][j]);

       }

    }

   

    //输入起始节点

    scanf("%d",&v);

   

    //计算dijkstra

    dijkstra(v,a,n);

   

    //打印源点到各点的最短距离

    int tmp = 0;

    for(i=1; i<=n; i++){

           printf("从%d到%d的距离是%d\n",v,i,dist[i]);

           tmp = i;

           printf("从%d到%d的最短路经过:");

           while(pre[tmp]>0 && pre[tmp] != v){

               printf("%d ",pre[tmp]);

               tmp = pre[tmp];

           }

           printf("%d\n",v);

    }

 

    system("pause");

    return 0;

}

 

时间复杂度分析:使用邻接矩阵,o(n*n)

 

最小生成树:prim

/*
6
0 6 1 5 10000 10000
6 0 5 10000 3 10000
1 5 0 5 6 1
5 10000 5 0 10000 2
10000 3 6 10000 0 6
10000 10000 1 2 6 0

*/

#include<stdio.h>
#include<stdlib.h>

#define NUM 10
#define MAX_VALUE 10000
 
int closest[NUM];  //closest[j]记录j和S中的邻接节点中距离最近的节点 
int lowcost[NUM];  //lowcost[j]记录j和S中最近邻接点的距离 
int s[NUM];        //s[j] = true,标记j在S中

int prim(int a[NUM][NUM],int n){
    //初始化
    int i,j,sum=0;
    for(i=1; i<=n; i++){
             closest[i] = 1;
             lowcost[i] = a[i][1];
             s[i] = false;
    }
    s[1] = true;

    for(j=1; j<n; j++){
        //寻找离S中节点最近的节点及距离
        int min = MAX_VALUE,v=1;
        for(i=1; i<=n; i++){
             if(!s[i] && min > lowcost[i]){
                      min = lowcost[i];
                      v = i;
             }
        }
        s[v] = true;
        sum += lowcost[v];
    
        //每添加一个新的节点到S,比较节点i和S中以前节点的最短距离 和 i和新增节点的最短距离,更新closest和c
        for(i=1; i<=n; i++){
             if(!s[i] && a[i][v] < lowcost[i]){
                      lowcost[i] = a[i][v];
             }
        }            
    }     
    
    return sum;                   
    
}

int main(){
    int i=1,j=1,n,a[NUM][NUM];
    
    //输入邻接矩阵 
    scanf("%d",&n);
    for(i=1; i<=n; i++){
       for(j=1; j<=n; j++){
           scanf("%d",&a[i][j]);
       }
    }
    
    printf("%d",prim(a,n));
    
    system("pause");
    return 0;

时间复杂度是:o(n*n)

说明:最小生成树primdijkstra相似,dijkstra保存了各点和源点的最近距离(dist)prim保存了各点和S中节点的最近邻接点及和最近邻接点的距离(closest, lowcost)。程序的书写过程相似,先初始化,再找最小的距离,再更新数组。

原创粉丝点击