zoukankan      html  css  js  c++  java
  • 活动选择的贪心算法与动态规划

    问题:

          有一个需要使用每个资源的n个活动组成的集合S= {a1,a2,···,an },资源每次只能由一个活动使用。每个活动a都有一个开始时间和结束时间,且 0<= s < f 。一旦被选择后,活动a就占据半开时间区间[s,f]。如果[s,f]和[s,f]互不重叠,则称两个活动是兼容的。该问题就是要找出一个由互相兼容的活动组成的最大子集。

          定义子集合Sij = { ak  S : f i <= sk < f k <= s j}, 即每个活动都在ai结束之后开始,在aj开始之前结束,亦即Sij包含了所有和ai和aj兼容的活动。 

          假设S中的活动已按照结束时间递增的顺序排列,则Sij具有如下的性质:

          1.当i <= j时,Sij 为空,

          2.假设ak属于Sij,那么ak将把Sij分解成两个子问题,Sij包含的活动集合就等于Sik中的活动+ak+Skj中的活动。从这里就可以看出Sij的最优子结构性质:Sij的最优解包含了子问题Sik和Skj的最优解。假设Sij的最大兼容活动子集为Aij,那么有Aij = Aik U  ak U Akj。整个活动选择问题的最优解也是S0,n+1的解。

          假设c[i,j]为Sij中最大兼容子集中的活动数。则有如下递归式:

          C[i,j] = 0                       如果 Sij 为空

                          C[i,j] = max{c[i,k] + c[k,j] +1 }                   i < k < j & ak  Sij  如果Sij 不为空

    根据这个递归式,可以得到一个动态规划解法。

    // greedy_algorithm.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include<iostream>
    #include<queue>
    using namespace std;
    
    #define NofActivity 11    //有效的活动数
    int c[NofActivity + 2][NofActivity + 2];   //c[i][j]存放第i个结束后第j个开始前兼容的活动个数
    int reme[NofActivity + 2][NofActivity + 2];   //记录哪几个活动满足条件
    //活动的结构/////////////////////////////////////////
    struct Activity
    {
    	int num;   //活动的标号
    	int start;   
    	int finish;
    };
    //活动已经按结束时间的早晚排好序
    //初始化活动结构数组,注意添加了了头和尾,活动有效数据只有11组,添加到13组的意思是找第0组结束之后,第12组开始之前可以兼容的那些活动组合
    Activity Act[NofActivity+2] = { {0,0,0},{ 1,1,4 },{2,3,5 },{3, 0,6 },{4, 5,7 },{5, 3,9 },{6, 5,9 },{7, 6,10 },{8, 8,11 },{9, 8,12 },{10, 2,14 },{11, 12,16 }, {12,24,24}};
    
    ///用队列来存储符合条件的活动,递归版本//////////////////////////////
    queue<Activity> select;
    void Recursive_activity_selector(Activity* Act, int k, int n)
    {
    	//查找k结束之后第一个结束的活动
    	int m = k + 1;
    	while (m <= n&&Act[m].start < Act[k].finish)
    		m++;
    	
    	//如果找到就把它入队,递归调用查找下一个
    	if (m <= n)
    	{
    		select.push(Act[m]);
    		Recursive_activity_selector(Act, m, n);
    	}
    }
    
    ///活动选择的迭代版本/////////////////////////////////////
    void Greedy_activity_selector(Activity* Act)
    {
    	//先把第一个入队
    	int n = NofActivity;
    	while (!select.empty())select.pop();
    	select.push(Act[1]);
    
    	//循环查找下一个满足条件的活动入队
    	int k = 1;
    	for (int i = 2; i <= n; i++)
    	{
    		if (Act[i].start > Act[k].finish) {
    			select.push(Act[i]);
    			k = i;
    		}
    	}
    }
    
    /////活动选择的动态规划版本//////////////////////////////////
    void activity_selector(Activity* Act)
    {
    	//初始化
    	for (int i = 0; i <= NofActivity+1; i++)
    	{
    		for (int j = 0; j <= NofActivity + 1; j++)
    		{
    			c[i][j] = 0;
    			reme[i][j] = 0;
    		}
    	}
    
    	//从长度为2的开始查找
    	for(int l=2;l<=NofActivity+2;l++)
    		for (int i = 0; i <= NofActivity-l+3; i++)    //注意开始要从虚拟的活动0开始,到虚拟的活动NofActivity+1结束,这是由c[i][j]的定义所决定的
    		{
    			int j = i + l - 1;
    			bool flag=false;                         //标志i,j之间是否含有兼容的活动
    			for (int k = i + 1; k < j; k++)
    			{
    				if (Act[k].start > Act[i].finish&&Act[k].finish < Act[j].start)  //c[i][j]定义的条件
    				{
    					if (c[i][j] < c[i][k] + c[k][j] + 1)
    					{
    						c[i][j] = c[i][k] + c[k][j] + 1;
    						//注意这里reme[i][j]的赋值,因为c[i][j] < c[i][k] + c[k][j] + 1,是小于号而不是小于等于,所以reme[i][j]会记录第一个满足条件的k,如reme[0][12]=1;
    						reme[i][j] = k;
    					}
    					flag = true;                                    //有置1
    				}
    			}
    			if (!flag)c[i][j] = 0;
    		}
    
    	//打印出c[i][j];
    	for (int i = 0; i <= NofActivity + 1; i++) {
    		for (int j = 0; j <= NofActivity + 1; j++)
    			cout << c[i][j] << ' ';
    		cout << endl;
    	}
    }
    
    //打印所选择的活动,act = reme[act][j]是由上诉对reme[i][j]的赋值规律所决定
    void printSelect(int i, int j)
    {
    	int act = reme[i][j];
    	while (act)
    	{
    		cout << act << '	';
    		act = reme[act][j];
    	}
    }
    
    
    int main()
    {
    	//Recursive_activity_selector(Act, 0, NofActivity);
    	/*
    	Greedy_activity_selector(Act);
    	while (!select.empty())
    	{
    		cout << select.front().num<< '	';
    		select.pop();
    	}
    	*/
    	activity_selector(Act);
    	printSelect(0, 12);
    	while (1);
        return 0;
    }
    

      

  • 相关阅读:
    Fiddler 的几个用法
    jQuery中ajax的使用和缓存问题解决 $getjson 与$get都会被IE缓存
    http与https的区别以及https的加密原理
    asp.net mvc通过预处理实现数据过滤和数据篡改。
    C# winform的WebBrowser非常规编程(强烈推荐)
    DES加密 java与.net可以相互加密解密的方法
    日常开发中常见的HTTP协议的状态码
    前端性能监控
    在手机的浏览器上通过连接打开App
    工作五年总结一二三流公司的常见特性
  • 原文地址:https://www.cnblogs.com/linear/p/6680132.html
Copyright © 2011-2022 走看看