貪心演算法講解例題:作用選取範圍問題問題描述
有一個需要使用每個資源的n個活動組成的集合S= {a1,a2,···,an },資源每次只能由一個活動使用。每個活動a都有一個開始時間和結束時間,且 0<= s < f < 。一旦被選擇後,活動a就佔據半開時間區間[s,f]。如果[s,f]和[s,f]互不重疊,則稱兩個活動是相容的。該問題就是要找出一個由互相相容的活動組成的最大子集。
11個活動按結束時間排序好,之後為:
s[]={1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12}; //開始時間
f[]={4, 5, 6, 7, 8, 9,10,11,12, 13,14}; //結束時間(遞增)
動態規劃求解:O(n3)
#include"stdio.h"
int c[13][13];//活動的個數 用來表示 從 i 到 j 符合規則的有幾個活動
int partition[13][13];//partition[i][j] 表示 使用哪個k 使得s[i][j]到最大相容集合
void dynamic_Activity_select(int s[],int f[],int n)
{
int i,j,k;
for(i=0;i<=n;i++)//初始化 情況下 沒有一個活動
for(j=0;j<=n;j++)
{
if(i==j)
c[i][j]=1;// 這個地方其實很關鍵 ,往往 貪心求解時 初始化容易初始化為 0
else
c[i][j]=0;
}
for(i=1;i<=n;i++)
{
for(j=2;j<=n;j++)
{
if(i>=j) //前面的 活動不能超過後面的活動
c[i][j]=0;
else
{
for(k=i+1;k<=j-1;k++) //從前面的下一個 活動開始
{
if(s[k] >= f[i] && f[k] <= s[j])// k 開始後 i 結束 k 結束 s 還沒有開始
if(c[i][j]<c[i][k]+c[k][j]+1)
{
c[i][j]=c[i][k]+c[k][j]+1;
partition[i][j]=k;
}
}
}
}
}
}
int main()
{
int s[]={0,1,3,0,5,3,5,6,8,8,2,12}; //這個地方容易出錯
int f[]={0,4,5,6,7,8,9,10,11,12,13,14};
dynamic_Activity_select(s,f,11);
int m=11;
int n=11;
for(int i=1;i<n;i++) //這裡可以自訂輸出
{
for (int j=1;j<m;j++)
printf("%3d", partition[i][j]);
printf("\n");
}
}
由於動態規劃時間複雜度高,用來求解這類問題,大材小用。所以提供以下貪心演算法
#include"stdio.h"
void recursive_activity_select(int s[],int f[],int i,int n)//遞迴的貪心演算法
{
int m=i+1;
while(m<=n&&s[m]<f[i]) //找出第一個合格 (有時候第一個並不是合格)
m++;
if(m<=n)
{
printf("%4d",m);
recursive_activity_select(s,f,m,n);
}
else
return ;
}
/*這個方法思想:尋找最早結束且與前一個 相容的 活動加入集合 i 在前 m 在後*/
void greedy_activity_select(int s[],int f[],int n)//迭代方法
{
int i=1;
/*由於是按照最早結束時間來排序 所以第一個肯定是最早結束的活動*/
printf("%4d",1);
for(int m=2;m<=n;m++) //也可以按照遞迴方法 中使用 while 迴圈(一個道理)
{
if(s[m]>=f[i])
{
printf("%4d",m);
i=m;
}
}
}
int main()
{
int s[]={0,1,3,0,5,3,5,6,8,8,2,12}; //這個地方容易出錯
int f[]={0,4,5,6,7,8,9,10,11,12,13,14};
printf("要選擇的活動為:");
// recursive_activity_select(s,f,0,11);//之所以注釋掉 是因為跟下面的方法重複 一次只能調試一個
greedy_activity_select(s,f,11);
}
評論這張轉寄至微博
轉寄至微博