日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

转:经典ACM算法

發布時間:2024/6/5 编程问答 90 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转:经典ACM算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

實驗一 統計數字問題
實驗二 最大間隙問題
實驗三 眾數問題
實驗四 半數集問題
實驗五 集合劃分問題
實驗六 最少硬幣問題
實驗七 編輯距離問題
實驗八 程序存儲問題
實驗九 最優服務次序問題
實驗十 汽車加油問題
實驗十一 工作分配問題
實驗十二 0-1背包問題
實驗十三 最小重量機器設計問題
實驗十四 最小權頂點覆蓋問題
實驗十五 集合相等問題
實驗十六 戰車問題

實驗一 統計數字問題

1、問題描述:
一本書的頁碼從自然數1 開始順序編碼直到自然數n。書的頁碼按照通常的習慣編排,每個頁碼都不含多余的前導數字0。例如,第6 頁用數字6 表示,而不是06 或006 等。數字計數問題要求對給定書的總頁碼n,計算出書的全部頁碼中分別用到多少次數字0,1, 2,…,9。
2、題目分析:
考慮由0,1,2,…,9組成的所有n位數。從n個0到n個9共有個n位數,在這些n位數中,0,1,2,…,9每個數字使用次數相同,設為。 滿足如下遞歸式:
由此可知,。
據此,可從低位向高位進行統計,再減去多余的0的個數即可。
3、算法設計:
定義數組a[10]存放0到9這10個數出現的次數,個位為第0位,第j位的數字為r。采用while循環從低位向高位統計:
a. 統計從個位算起前j位0~9個數;
b. 如果j+1位為0,去掉第j+1位補0個數;
c. 統計第j+1位出現1~(r-1)個數;
d. 統計第j+1位出現r個數。
4、源程序:

#include
int main()
{
long int sn[10];
int i,n,c,k,s,pown;
for(i=0;i<10;i++) br=""> sn[i]=0;
cin>>n;
for(k=s=0,pown=1;n>0;k++,n/=10,pown*=10)
{
c=n%10;
for(i=0;i<10;i++) br=""> sn[i]+=c*k*(pown/10);
for(i=0;i sn[i]+=pown;
sn[c]+=1+s;
sn[0] -=pown;
s+=c*pown;
}
for(i=0;i<10;i++) br=""> cout< }

5、算法分析:
函數count()的復雜度為O(1),主函數調用count(),故該算法的時間復雜度為O(1)。

實驗二 最大間隙問題



1、問題描述:
最大間隙問題:給定n 個實數x1 , x2 ,... , xn,求這n 個數在實軸上相鄰2 個數之間的最大差值。假設對任何實數的下取整函數耗時O(1),設計解最大間隙問題的線性時間算法。 對于給定的n 個實數x1 , x2 ,... , xn,編程計算它們的最大間隙。

2、題目分析:
考慮到實數在實軸上按大小順序排列,先對這n個數排序,再用后面的數減去前面的數,即可求出相鄰兩數的差值,找出差值中最大的即為最大差值。

3、算法設計:
a. 用快速排序算法對這n個數排序,快速排序算法是基于分治策略的一個排序算
法。其基本思想是,對于輸入的子數組a[p:r],按以下三個步驟進行排序:
①分解:以a[p]為基準元素將a[p:r]劃分為3段a[p:q-1],a[q]和a[q+1:r],使a[p:q-1]中任何一個元素小于等于a[p],而a[q+1:r]中任何一個元素大于等于a[q]。下標q在劃分過程中確定。
②遞歸求解:通過遞歸調用快速排序算法分別對a[p:q-1]和a[q+1:r]進行排序。
③合并:由于對a[p:q-1]和a[q+1:r]的排序是就地進行的,所以在a[p:q-1]和
a[q+1:r]都已排好的序后,不需要執行任何計算,a[p:r]就已排好序。
b. 用函數maxtap()求出最大差值。

4、源程序:

#include
#include
double a[1000000];

template
void swap(Type &x,Type &y)
{
Type temp=x;
x=y;
y=temp;
}

template
int Partition(Type *a,int low,int high)
{
Type pivotkey;
int mid=(low+high)/2;
if((a[low]a[mid]&&a[mid]>a[high]))
swap(a[low],a[mid]);
if((a[low]a[high])||(a[low]>a[high]&&a[mid] swap(a[low],a[high]);
pivotkey=a[low];
int i=low;
int j=high+1;
while(true)
{
while(a[++i] while(a[--j]>pivotkey);
if(i>=j) break;
swap(a[i],a[j]);
}
a[low]=a[j];
a[j]=pivotkey;
return j;
}

template
void Quicksort(Type *a,int low,int high)
{
if(low {
int q=Partition(a,low,high);
Quicksort(a,low,q-1);
Quicksort(a,q+1,high);
}
}

template
Type maxtap(Type *a,int n)
{
Type maxtap=0;
for(int i=0;i maxtap=(a[i+1]-a[i])>maxtap (a[i+1]-a[i]):maxtap;
return maxtap;
}

main()
{
int i,n;
scanf("%d",&n);
for(i=0;i scanf("%lf",&a[i]);
Quicksort(a,0,n-1);
printf("%lf",maxtap(a,n));
return 0;


5、算法分析:
快速排序的運行時間與劃分是否對稱有關,其最壞情況發生在劃分過程產生的兩個區域分別包含n-1個元素和1個元素的時候。由于函數Partition的計算時間為O(n),所以如果算法Partition的每一步都出現這種不對稱劃分,則其計算時間復雜性T(n)滿足:

解此遞歸方程可得。
在最好情況下,每次劃分所取得基準都恰好為中值,即每次劃分都產生兩個大小為n/2的區域,此時,Partition的計算時間T(n)滿足:

其解為。
可以證明,快速排序算法在平均情況下的時間復雜性也是。

實驗三 眾數問題



1、問題描述:
給定含有n個元素的多重集合S,每個元素在S中出現的次數稱為該元素的重數。多重集S中重數最大的元素稱為眾數。例如,S={1,2,2,2,3,5}。多重集S的眾數是2,其重數為3。 對于給定的由n 個自然數組成的多重集S,編程計算S 的眾數及其重數。

2、題目分析:
題目要求計算集合中重復出現最多的數以及該數出現的次數,若兩個數出現的次數相同,則取較小的數。先對集合中的元素從小到大排序,再統計重數,找出最大的重數以及它對應的元素。

3、算法設計:
a. 建立數組a[n]存儲集合中的元素;
b. 調用庫函數sort(a,a+n)對集合中的元素從小到大排序;
c. 采用for循環依次對排序后的元素進行重數統計:
①用m標記元素,用s統計該元數的重數,分別用 max和t標記出現的最大重數和該重數對應的元素。
②若當前元素不同于前一個元素,且前一個元素的重數s>max,更新max和t。

4、源程序:

#include
using namespace std;
#include

main()
{
int n,i,t,m=0,s,max=0,*a;
scanf("%d",&n);
a=new int[n];
for(i=0;i scanf("%d",&a[i]);
sort(a,a+n);
for(i=0;i {
if(m!=a[i])
s=1;
else
s++;
m=a[i];
if(s>max)
{
t=m;
max=s;
}
}
printf("%d\n%d\n",t,max);
return 0;
}


5、算法分析:
主函數內調用的庫函數sort(a,a+n)的時間復雜度為,for循環的時間雜度為Ο(n),故該算法的時間雜度為。

實驗四 半數集問題



1、問題描述:
給定一個自然數n,由n開始可以依次產生半數集set(n)中的數如下:(1) n∈set(n);(2) 在n的左邊加上一個自然數,但該自然數不能超過最近添加的數的一半;(3) 按此規則進行處理,直到不能再添加自然數為止。
例如:set(6)={6,16,26,126,36,136},半數集set(6)中有6 個元素。注意半數集
是多重集,對于給定的自然數n,編程計算半數集set(n)中的元素個數。

2、題目分析:
題目要求輸入有多行,每行給出一個整數n (0 < n < 1000) ,輸入以文件結束標志EOF結束。半數集set(n)中的元素個數即為所有半數集set(j) (j<=n 2="" 1="" br="">
3、算法設計:
a. 用數組count[i]計算set(i)中的元素個數,即計算所有j<=i 2="" set="" j="" br=""> 其自身的個數之和;
b. 用數組count_sum[i]計算所有j<=i的set(j) br=""> c. 采用for循環分別對count[i]、count_sum[i]進行累加,即可求出半數集set(n)中的
元素個數。

4、源程序:

#include

int set(int n)
{
int i,count[1001],count_sum[1001];
count[1]=1;
count_sum[1]=1;
for(i=2;i<=n;i++) br=""> {
count[i]=count_sum[i/2]+1;
count_sum[i]=count_sum[i-1]+count[i];
}
return count[n];
}

main()
{
int n;
while(scanf("%d",&n)!=EOF)
printf("%d\n",set(n));
return 0;
}


5、算法分析:
函數set(n)的時間復雜度為Ο(n),主函數調用函數set(n),故該算法的時間復雜度為Ο(n)。

實驗五 集合劃分問題




2、題目分析:
題目要求計算n個元素的集合共有多少個劃分(其中每個劃分都由不同的非空子集組成),n個元素的集合劃分為m個塊的劃分數為F(n,m)=F(n-1,m-1)+m*F(n-1,m),m從1到n的劃分個數相加即可求得總的劃分數。

3、算法設計:
a. 這一題的結果數很大,需要使用64位長整型:__int64;
b. 函數div()采用遞歸的方式計算n個元素的集合劃分為i個塊的劃分數:
①div (n,1)=1,div (n,n)=1;
②div (n,i)= div (n-1,i-1)+i*div (n-1,i)
c. 主函數采用for循環調用函數div(n,i)(1≤i≤n)對劃分數進行累加統計。
4、源程序:

#include

__int64 div(__int64 n,__int64 i)
{
if(i==1||i==n)
return 1;
else return div(n-1,i-1)+i*div(n-1,i);
}

main()
{
__int64 i,n,s=0;
scanf("%I64d",&n);
for(i=1;i<=n;i++) br=""> s=s+div(n,i);
printf("%I64d",s);
return 0;
}


5、算法分析:
函數div()的時間復雜度為,主函數內for循環的時間復雜度為O(n),函數div()嵌套在for循環內,故該算法的時間復雜度為。

實驗七 編輯距離問題



1、問題描述:
設A 和B 是2 個字符串。要用最少的字符操作將字符串A 轉換為字符串B。這
里所說的字符操作包括 (1)刪除一個字符; (2)插入一個字符; (3)將一個字符改為另
一個字符。將字符串A變換為字符串B 所用的最少字符操作數稱為字符串A到B 的編
輯距離,記為 d(A,B)。試設計一個有效算法,對任給的2 個字符串A和B,計算出它
們的編輯距離d(A,B)。

2、題目分析:
題目要求計算兩字符串的編輯距離,可以采用動態規劃算法求解,由最優子結構性質可建立遞歸關系如下:
其中數組d[i][j] 存儲長度分別為i、j的兩字符串的編輯距離;用edit標記所比較
的字符是否相同,相同為0,不同為1;用m、n存儲字符串a、b的長度。

3、算法設計:
a. 函數min()找出三個數中的最小值;
b. 函數d()計算兩字符串的編輯距離:
①用edit標記所比較的字符是否相同,相同為0,不同為1;
②分別用m、n存儲字符串a、b的長度,用數組d[i][j] 存儲長度分別為i、j的兩字符串的編輯距離,問題的最優值記錄于d[n][m]中;
③利用遞歸式寫出計算d[i][j]的遞歸算法。

4、源程序:

#include
using namespace std;

int min(int a, int b, int c)
{
int temp = (a < b a : b);
return (temp < c temp : c);
}

int d(char* a, char* b)
{
int m = strlen(a);
int n = strlen(b);
int i,j ,temp;
int **d;
d=(int **)malloc(sizeof(int *)*(m+1));
for(i=0;i<=m;i++) br=""> {
d[i]=(int *)malloc(sizeof(int)*(n+1));
}
d[0][0]=0;
for(i=1;i<=m;i++) br=""> d[i][0]=i;
for(j=1;j<=n;j++) br=""> d[0][j]=j;
for( i=1;i<=m;i++) br=""> {
for(j=1;j<=n;j++) br=""> {
int edit=( a[i-1] == b[j-1] 0 : 1);
d[i][j]=min((d[i-1][j-1]+edit),
(d[i][j-1]+1),(d[i-1][j]+1));
}
}
temp = d[m][n];
free(d);
return temp;
}

main()
{
char a[10000],b[10000];
scanf("%s\n%s",a,b);
printf("%d",d(a,b));
return 0;
}


5、算法分析:
函數d()采用了雙重循環,里層的for循環復雜度為O(n),外層的for循環復雜度為O(m),主函數調用了函數d(),故該算法的時間復雜度為O(mn)。

實驗八 程序存儲問題



1、問題描述:
設有n 個程序{1,2,…, n }要存放在長度為L的磁帶上。程序i存放在磁帶上的長度是i l , 1 ≤i ≤n。程序存儲問題要求確定這n 個程序在磁帶上的一個存儲方案,使得能夠在磁帶上存儲盡可能多的程序。對于給定的n個程序存放在磁帶上的長度,編程計算磁帶上最多可以存儲的程序數。

2、題目分析:
題目要求計算給定長度的磁帶最多可存儲的程序個數,先對程序的長度從小到大排序,再采用貪心算法求解。

3、算法設計:
a. 定義數組a[n]存儲n個程序的長度,s為磁帶的長度;
b. 調用庫函數sort(a,a+n)對程序的長度從小到大排序;
c. 函數most()計算磁帶最多可存儲的程序數,采用while循環依次對排序后的程序
長度進行累加,用i計算程序個數,用sum計算程序累加長度(初始i=0,sum=0):
① sum=sum+a[i];
② 若sum<=s,i加1,否則i為所求; br=""> ③ i=n時循環結束;
d. 若while循環結束時仍有sum<=s,則n為所求。 br="">
4、源程序:

#include
using namespace std;
#include
int a[1000000];

int most(int *a,int n,int s)
{
int i=0,sum=0;
while(i {
sum=a[i]+sum;
if(sum<=s) br=""> i++;
else return i;
}
return n;
}

main()
{
int i,n,s;
scanf("%d %d\n",&n,&s);
for(i=0;i scanf("%d",&a[i]);
sort(a,a+n);
printf("%d",most(a,n,s));
return 0;
}

5、算法分析:
函數most()的時間雜度為Ο(n),主函數調用的庫函數sort(a,a+n)的時間復雜度為,且主函數調用函數most(),故該算法的時間雜度為。

實驗九 最優服務次序問題



1、問題描述:
設有n 個顧客同時等待一項服務。顧客i需要的服務時間為ti ,1 ≤i≤n。應如何安排n 個顧客的服務次序才能使平均等待時間達到最小 平均等待時間是n 個顧客等待服務時間的總和除以n。對于給定的n個顧客需要的服務時間,編程計算最優服務次序。

2、題目分析:
考慮到每個顧客需要的服務時間已給定,要計算最優服務次序,可以先對顧客需要的服務時間由短到長排序,再采用貪心算法求解。

3、算法設計:
a. 定義數組a[n]存儲n個顧客需要的服務時間;
b. 調用庫函數sort(a,a+n)對顧客需要的服務時間由短到長排序;
c. 函數time()計算最小平均等待時間,采用while循環依次對顧客等待服務時間進
行累加,用a[i]標記第i+1個顧客需要的服務時間,用b[i]計算第i+1個顧客的等待服務
時間,用t計算前i+1個顧客等待服務時間的總和(初始i=1, t=a[0],b[0]=a[0]):
① b[i]=a[i]+b[i-1],t=b[i]+t;
② i++;
③ i=n時循環結束;
d. t/n為所求。

4、源程序:

#include
using namespace std;
#include
int a[1000000],b[1000000];
double time(int *a,int n)
{
int i=1,j=0;
double t=a[0];
b[0]=a[0];
while(i {
b[i]=a[i]+b[i-1];
t=b[i]+t;
i++;
}
return t/n;
}

main()
{
int i,n;
scanf("%d",&n);
for(i=0;i scanf("%d",&a[i]);
sort(a,a+n);
printf("%0.2lf",time(a,n));
return 0;
}


5、算法分析:
函數time()的時間雜度為Ο(n),主函數調用的庫函數sort(a,a+n)的時間復雜度為,且主函數調用函數time(),故該算法的時間雜度為。

實驗十 汽車加油問題



1、問題描述:
一輛汽車加滿油后可行駛n公里。旅途中有若干個加油站。設計一個有效算法,指出應在哪些加油站停靠加油,使沿途加油次數最少。并證明算法能產生一個最優解。對于給定的n和k個加油站位置,編程計算最少加油次數。

2、題目分析:
題目要求編程計算最少加油次數,若無法到達目的地,則輸出“No Solution”。該題 可以采用貪心算法求解,從出發地開始進行判別:油足夠則繼續行駛;油不夠則加油,計算加油次數;油滿仍不夠則“No Solution”。

3、算法設計:
a. n表示汽車加滿油后可行駛n公里,k表示出發地與目的地之間有k個加油站;
b. 定義數組a[k+1]存儲加油站之間的距離:用a[i]標記第i個加油站與第i+1個加
油站之間的距離(第0 個加油站為出發地,汽車已加滿油;第k+1 個加油站為目的地);
c. 用m計算加油次數,用t標記在未加油的情況下汽車還能行駛t公里,采用for
循環從出發地開始(即i=0)依次計算加油次數:
① 若a[i]>n,則輸出“No Solution”;
② 若t③ 行駛a[i]公里后,汽車還能行駛t- a[i]公里;
④ i=k+1時循環結束;
d. m即為所求。

4、源程序:

#include
main()
{
int i,n,k,t,m,a[10000];
scanf("%d%d",&n,&k);
for(i=0;i<=k;i++) br=""> scanf("%d",&a[i]);
m=0;
t=n;
for(i=0;i<=k;i++) br=""> {
if(a[i]>n)
{
printf("No Solution");
break;
}
else if(t{
t=n; m++;
}
t=t-a[i];
}
if(i==k+1)
printf("%d",m);
return 0;
}


5、算法分析:
主函數內for循環的時間復雜度為O(k),故該算法的時間雜度為O(k)。

實驗十一 工作分配問題



1、問題描述:
設有n件工作分配給n個人。將工作i分配給第j個人所需的費用為cij 。試設計一個算法,為每一個人都分配1 件不同的工作,并使總費用達到最小。設計一個算法,對于給定的工作費用,計算最佳工作分配方案,使總費用達到最小。

2、題目分析:
考慮到每個人對應的每項工作費用已給定,要計算最佳工作分配方案,使總費用達到最小,可以采用回溯法求解。由于回溯法是對解空間的深度優先搜索,因此此題可用排列樹來表示解空間。

3、算法設計:
a. 用c[i][j]存儲將工作i分配給第j個人所需的費用,用v[j] 標記第j個人是否已分
配工作;
b. 用遞歸函數backtrack (i, total)來實現回溯法搜索排列樹(形式參數i表示遞歸深
度,n用來控制遞歸深度,形式參數total表示當前總費用,s表示當前最優總費用):
① 若total>=s,則不是最優解,剪去相應子樹,返回到i-1層繼續執行;
② 若i >n,則算法搜索到一個葉結點,用s對最優解進行記錄,返回到i-1層
繼續執行;
③ 采用for循環針對n個人對工作i進行分配(1≤j≤n):
1> 若v[j]==1 ,則第j個人已分配了工作,找第j+1個人進行分配;
2> 若v[j]==0,則將工作i分配給第j個人(即v[j]=1 ),對工作i+1調用遞歸函數backtrack(i+1,total+c[i][j])繼續進行分配;
3> 函數backtrack(i+1,total+c[i][j])調用結束后則返回v[j]=0,將工作i對
第j+1個人進行分配;
4> 當j>n時,for循環結束;
④ 當i=1時,若已測試完c[i][j]的所有可選值,外層調用就全部結束;
c. 主函數調用一次backtrack(1,0)即可完成整個回溯搜索過程,最終得到的s即為
所求最小總費用。

4、源程序:

#include
#define MAX 1000;
int n,c[21][21],v[21],s,total;

void backtrack(int i,int total)
{
int j;
if(total>=s)
return;
if(i>n)
{
s=total;
return;
}
else
for(j=1;j<=n;j++) br=""> {
if(v[j]==1)
continue;
if(v[j]==0)
{
v[j]=1; backtrack(i+1,total+c[i][j]);
v[j]=0;
}
}
}

main()
{
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++) br=""> {
for(j=1;j<=n;j++) br=""> {
scanf("%d",&c[i][j]);
v[j]=0;
}
}
s=MAX;
backtrack(1,0);
printf("%d",s);
return 0;
}


5、算法分析:
遞歸函數backtrack (i, total)遍歷排列樹的時間復雜度為O(n!),主函數調用遞歸函
數backtrack(1,0),故該算法的時間復雜度為O(n!)。

實驗十二 0-1背包問題



1、問題描述:
給定n 種物品和一個背包。物品i 的重量是wi ,其價值為vi ,背包的容量為C。應如何選擇裝入背包的物品,使得裝入背包中物品的總價值最大 在選擇裝入背包的物品時,對每種物品i只有2 種選擇,即裝入背包或不裝入背包。不能將物品i 裝入背包多次,也不能只裝入部分的物品i。 0-1 背包問題形式化描述:給定C>0, wi >0, vi >0,1≤i≤n,要求n 元0-1向量( x1 , x2 ,…, xn ),xi = 0或1,1≤i≤n,使得,而且達到最大。
2、題目分析:
考慮到每種物品只有2 種選擇,即裝入背包或不裝入背包,并且物品數和背包容量已給定,要計算裝入背包物品的最大價值和最優裝入方案,可用回溯法搜索子集樹的算法進行求解。

3、算法設計:
a. 物品有n種,背包容量為C,分別用p[i]和w[i]存儲第i種物品的價值和重量,用
x[i]標記第i種物品是否裝入背包,用bestx[i]存儲第i種物品的最優裝載方案;
b. 用遞歸函數Backtrack (i,cp,cw)來實現回溯法搜索子集樹(形式參數i表示遞歸深
度,n用來控制遞歸深度,形式參數cp和cw表示當前總價值和總重量,bestp表示當前
最優總價值):
① 若i >n,則算法搜索到一個葉結點,判斷當前總價值是否最優:
1> 若cp>bestp,更新當前最優總價值為當前總價值(即bestp=cp),更新
裝載方案(即bestx[i]=x[i]( 1≤i≤n));
② 采用for循環對物品i裝與不裝兩種情況進行討論(0≤j≤1):
1> x[i]=j;
2> 若總重量不大于背包容量(即cw+x[i]*w[i]<=c),則更新當前總價 br=""> 值和總重量(即cw+=w[i]*x[i],cp+=p[i]*x[i]), 對物品i+1調用遞歸函
數Backtrack(i+1,cp,cw) 繼續進行裝載;
3> 函數Backtrack(i+1,cp,cw)調用結束后則返回當前總價值和總重量
(即 cw-=w[i]*x[i],cp-=p[i]*x[i]);
4> 當j>1時,for循環結束;
③ 當i=1時,若已測試完所有裝載方案,外層調用就全部結束;
c. 主函數調用一次backtrack(1,0,0)即可完成整個回溯搜索過程,最終得到的bestp和bestx[i]即為所求最大總價值和最優裝載方案。

4、源程序:

#include
int n,c,bestp;
int p[10000],w[10000],x[10000],bestx[10000];

void Backtrack(int i,int cp,int cw)
{
int j;
if(i>n)
{
if(cp>bestp)
{
bestp=cp;
for(i=0;i<=n;i++) br=""> bestx[i]=x[i];
}
}
else
for(j=0;j<=1;j++) br=""> {
x[i]=j;
if(cw+x[i]*w[i]<=c) br=""> {
cw+=w[i]*x[i];
cp+=p[i]*x[i];
Backtrack(i+1,cp,cw);
cw-=w[i]*x[i];
cp-=p[i]*x[i];
}
}
}

main()
{
int i;
bestp=0;
scanf("%d%d",&n,&c);
for(i=1;i<=n;i++) br=""> scanf("%d",&p[i]);
for(i=1;i<=n;i++) br=""> scanf("%d",&w[i]);
Backtrack(1,0,0);
printf("Optimal value is\n");
printf("%d\n",bestp);
for(i=1;i<=n;i++) br=""> printf("%d ",bestx[i]);
return 0;
}


5、算法分析:
遞歸函數Backtrack (i,cp,cw)遍歷子集樹的時間復雜度為,主函數調用遞歸函
數Backtrack(1,0,0),故該算法的時間復雜度為。

實驗十三 最小重量機器設計問題



1、問題描述:
設某一機器由n個部件組成,每一種部件都可以從m個不同的供應商處購得。設 wij 是從供應商j 處購得的部件i的重量,cij 是相應的價格,給出總價格不超過d的最小重量機器設計。

2、題目分析:
考慮到從每一處供應商購得每一種部件的重量和相應價格以及總價格的上限已給定,要設計最小重量機器方案計算最小重量,可用回溯法搜索排列樹的算法進行求解。

3、算法設計:
a. 部件有n個,供應商有m個,分別用w[i][j]和c[i][j]存儲從供應商j 處購得的部
件i的重量和相應價格,d為總價格的上限。
b. 用遞歸函數Knapsack(i,cs,ws)來實現回溯法搜索排列樹(形式參數i表示遞歸深
度,n用來控制遞歸深度,形式參數cs和ws表示當前總價格和總重量,bestw表示當前
最優總重量):
① 若cs>d,則為不可行解,剪去相應子樹,返回到i-1層繼續執行;
② 若ws>=bestw,則不是最優解,剪去相應子樹,返回到i-1層繼續執行;
③ 若i >n,則算法搜索到一個葉結點,用bestw對最優解進行記錄,返回到
i-1層繼續執行;
④ 采用for循環對部件i從m個不同的供應商購得的情況進行討論(1≤j≤m):
1> 調用遞歸函Knapsack(i+1,cs+c[i][j],ws+w[i][j])對部件i+1進行購買;
2> 當j>m時for循環結束;
⑤ 當i=1時,若已測試完所有購買方案,外層調用就全部結束;
c. 主函數調用一次Knapsack(1,0,0)即可完成整個回溯搜索過程,最終得到的bestw
即為所求最小總重量。

4、源程序:

#include
#define MIN 1000
int n,m,d,cs,ws,bestw;
int w[100][100],c[100][100];

void Knapsack(int i,int cs,int ws)
{
int j;
if(cs>d)
return;
else if(ws>=bestw)
return;
else if(i>n)
{
bestw=ws;
return;
}
else
{
for(j=1;j<=m;j++) knapsack="" i="" 1="" cs="" c="" j="" ws="" w="" br=""> }
}

main()
{
int i,j;
scanf("%d%d%d",&n,&m,&d);
for(i=1;i<=n;i++) br=""> {
for(j=1;j<=m;j++) br=""> scanf("%d",&c[i][j]);
}
for(i=1;i<=n;i++) br=""> {
for(j=1;j<=m;j++) br=""> scanf("%d",&w[i][j]);
}
bestw=MIN;
Knapsack(1,0,0);
if(bestw==MIN)
printf("No Solution!");
else
printf("%d",bestw);
return 0;
}


5、算法分析:
遞歸函數Knapsack(i,cs,ws)遍歷排列樹的時間復雜度為O(n!);主函數的雙重for
循環的時間復雜度為O(mn), 且主函數調用遞歸函數Knapsack(1,0,0),故該算法的時
間復雜度為O(n!)。

實驗十四 最小權頂點覆蓋問題



1、問題描述:
給定一個賦權無向圖G=(V,E),每個頂點v∈V都有一個權值w(v)。如果U包含于V,且對于(u,v)∈E 有u∈U 且v∈V-U,則有v∈K.如:U = {1}, 若有邊(1,2), 則有2屬于K. 若有集合U包含于V使得U + K = V, 就稱U 為圖G 的一個頂點覆蓋。G 的最小權頂點覆蓋是指G 中所含頂點權之和最小的頂點覆蓋。

2、題目分析:
考慮到每個頂點有在與不在頂點覆蓋集中兩種情況,并且每個頂點的權值已給定,
要計算最小權頂點覆蓋的頂點權之和,可用回溯法搜索子集樹的算法進行求解。

3、算法設計:
a. 給定的圖G 有n 個頂點和m條邊,用w[i]存儲頂點i的權值,用e[i][j]標記兩頂
點為i和j的邊是否存在,用c[i]標記頂點i是否在頂點覆蓋集中;
b. 用函數cover()判斷圖G 是否被頂點覆蓋(用t標記):
① 初始t=0;
② 采用while循環對每個頂點i(1≤i≤n)進行討論:
1> 若頂點i不在頂點覆蓋集中(即c[i]==0),則查找與之有邊連接的頂點j(即e[i][j]==1),判斷所有頂點j:
若存在頂點j在頂點覆蓋集中(即c[j]==0),則t=1;
若所有頂點j都不在頂點覆蓋集中(即t==0),則圖G 未被頂點
覆蓋(return 0);
2> 當i>n時循環結束;
③ return 1;
c. 用遞歸函數cut(i, s) 來實現回溯法搜索子集樹(形式參數i表示遞歸深度,n用
來控制遞歸深度,形式參數s表示當前頂點權之和):
① 若s>=bestw,則不是最優解,剪去相應子樹,返回到i-1層繼續執行;
② 若i >n,則算法搜索到一個葉結點,調用函數cover()對圖G進行判斷:
若cover()為真,則用bestw對最優解進行記錄,返回到i-1層繼續執行;
③ 對頂點i分在與不在頂點覆蓋集中兩種情況進行討論:
1> 若頂點i不在頂點覆蓋集中(即c[i]==0),則調用函數cut(i+1,s);
2> 若頂點i在頂點覆蓋集中(即c[i]==1),則調用函數cut(i+1,s+w[i]);
④ 當i=1時,若已測試完所有頂點覆蓋方案,外層調用就全部結束;
d. 主函數調用一次cut(1,0)即可完成整個回溯搜索過程,最終得到的bestw即為所
求最小頂點權之和。

4、源程序:

#include
#define MIN 100000
int m,n,u,v,bestw;
int e[100][100],w[100],c[100];

int cover()
{
int i,j,t;
i=1;
while (i<=n) br=""> {
t=0;
if(c[i]==0)
{
j=1;
while(j {
if(e[j][i]==1&&c[j]==1)
t=1;
j++;
}
j++;
while(j<=n) br=""> {
if(e[i][j]==1&&c[j]==1)
t=1;
j++;
}
if(t==0)
return 0;
}
i++;
}
return 1;
}

void cut(int i,int s)
{
if(s>=bestw)
return;
if(i>n)
{
if(cover())
bestw=s;
return;
}
c[i]=0;
cut(i+1,s);
c[i]=1;
cut(i+1,s+w[i]);
}

main()
{
int i,j,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) br=""> {
scanf("%d",&w[i]);
c[i]=0;

}

for(i=1;i<=n;i++) br=""> for(j=1;j<=n;j++) br=""> e[i][j]=0;

for(k=1;k<=m;k++) br=""> {
scanf("%d%d",&u,&v);
e[u][v]=1;
}

bestw=MIN;
cut(1,0);
printf("%d",bestw);
return 0;
}



5、算法分析:
函數cover()的雙重while循環的時間復雜度為,遞歸函數cut(i, s)遍歷子集
樹的時間復雜度為,且函數cover()嵌套在函數cut(i, s)內,所以遞歸函數cut(i, s)
的時間復雜度為;主函數的雙重for循環的復雜度為,且主函數調用
遞歸函數cut(1, 0),故該算法的時間復雜度為。

實驗十五 集合相等問題



1、問題描述:
給定2個集合S和T,試設計一個判定S和 T是否相等的蒙特卡羅算法。

2、題目分析:
題目要求用蒙特卡羅算法進行求解,隨機選擇集合S中的元素與集合T中的元素進行比較,若隨機選擇很多次都能從集合T中找到與之對應的相等,則集合S和T相等。

3、算法設計:
a. 蒙特卡羅算法Majority對從集合S中隨機選擇的數組元素x,測試集合T中是否有
與之相等的元素:若算法返回的結果為true,則集合T中有與x相等的元素;若返回false,
則集合T中不存在與x相等的元素,因而集合S和 T不相等。
b. 算法MajorityMC重復調用次算法Majority,調用過程中若Majority
返回true則繼續調用,否則可以判定集合S和 T不相等(MajorityMC返回false)。
c. 主函數調用算法MajorityMC:若返回true,則集合T和集合S相等;若返回false,
則集合S和 T不相等。

4、源程序:

#include
#include
#include
#include

bool Majority(int *S,int *T,int n)
{
int i,j,x;
bool k;
time_t t;
//利用隨機函數rand求0—n-1的隨機數i
srand((unsigned)time(&t));
i=rand()%(n)-1;
x=S[i];
k=0;
for(j=0;j {
if(T[j]==x)
k=1;
}
return k;
}

bool MajorityMC(int *S,int *T,int n)
{//重復次調用算法Majority
int k;
double e;
e=0.00001;
//利用函數ceil求
k=(int)ceil(log(1/e));
for(int i=1;i<=k;i++) br=""> {
if(!Majority(S,T,n))
return 0;
}
return 1;
}

main()
{
int i,n;
int S[100000],T[100000];
scanf("%d",&n);
for(i=0;i scanf("%d",&S[i]);
for(i=0;i scanf("%d",&T[i]);
if(MajorityMC(S,T,n))
printf("YES");
else printf("NO");
return 0;
}

5、算法分析:
蒙特卡羅算法Majority的時間復雜度為O(n);算法MajorityMC重復調用
次算法Majority,時間復雜度為;主函數調用算法MajorityMC,故該算
法的時間復雜度為。


實驗十六 戰車問題



1、問題描述:
在 n×n格的棋盤上放置彼此不受攻擊的車。按照國際象棋的規則,車可以攻擊與之處在同一行或同一列上的棋子。在棋盤上的若干個格中設置了堡壘,戰車無法穿越 堡壘攻擊別的戰車。對于給定的設置了堡壘的 n×n格棋盤,設計一個概率算法,在棋盤上放置盡可能多彼此不受攻擊的車。

2、題目分析:
從第一行開始隨機產生一個位置,看戰車在該位置上能否放置,分三種情況討論:
a. 該隨機位置對應在棋盤上是一個堡壘,則不能放置;
b. 該位置與前面放置的戰車相沖突,即在受前面放置戰車攻擊的位置上,則
也不能放置;
c. 該隨機位置不受攻擊也不是堡壘,則可以放置;
如果不能放置,則重新產生一個隨機位置再判斷,如果可以放置, 則放在該位置上并記錄戰車個數和該戰車可能攻擊的位置。以這種方法從第一行開始放置,第一行不能放置戰車后再放第二行,直至第n行結束。
3、算法設計:
a. 建立隨機數類CRandomNumber;
b. 函數CheckPlace判斷是否可以放入戰車,同時查看所放戰車的攻擊位置;
c. 函數MaxChes產生隨機位置,放置并記錄戰車數。

4、源程序:

#include
#include
#include
#include
const unsigned long multiplier=1194211693L;
const unsigned long adder=12345L;

class CRandomNum
{
private:
unsigned long nSeed;
public:
CRandomNum(unsigned long s=0);
unsigned short Random(unsigned long n);
};

CRandomNum::CRandomNum(unsigned long s)
{
if(s==0)
nSeed=time(0);
else
nSeed=s;
}

unsigned short CRandomNum::Random(unsigned long n)
{
nSeed=multiplier*nSeed+adder;
return (unsigned short)((nSeed>>16)%n);
}

bool CheckPlaceChe(int **b,char **a,int x,int y,int n)
{
if(b[x][y]==1)return false;
else
{
b[x][y]=1;
if((y>=1)&&(y<=n)) br=""> { //上搜索
for(int i=y-1;i>=0;i--)
{
if ((b[x][i]==1) &&(a[x][i]=='X'))
break;
else b[x][i]=1;
}
}

if((y<=n-1)&&(y>=0))
{ //下搜索
for(int i=y+1;i {
if((b[x][i]==1)&&(a[x][i]=='X'))
break;
else b[x][i]=1;
}
}

if((x>=1)&&(x<=n)) br=""> { //左搜索
for(int j=x-1;j>=0;j--)
{
if((b[j][y]==1) &&(a[j][y]=='X'))
break;
else b[j][y]=1;
}
}
if((x>=0)&&(x<=n-1)) br=""> { //右搜索
for(int j=x+1;j {
if((b[j][y]==1) &&(a[j][y]=='X'))
break;
else b[j][y]=1;
}
}
return true;
}
}
int MaxChes(int **b,char**a,int *x,int n)
{
int max1=0;
static CRandomNum rnd;
for(int i=0;i {
bool flag=false;
do{
for(int j=0;j {
if(b[i][j]==0){ flag=true;break;} else {flag=false;continue;}
}
if(flag)
{
x[i]=rnd.Random(n);
if(CheckPlaceChe(b,a,i,x[i],n))
++max1;
}
}while(flag);
}
return max1;
}

int main()
{
int n,i,j;
cin>>n;
char **a=new char*[n];
for(i=0;i a[i]=new char[n];
for(i=0;i for(j=0;j cin>>a[i][j];
int **b=new int*[n];
for(i=0;i b[i]=new int[n];
int max=0;
int *x=new int[n];
for(i=0;i x[i]=0;

int t=0;
while(t<15000) br=""> {
for(i=0;i { for(int j=0;j {
if(a[i][j]=='X')
b[i][j]=1;
else
b[i][j]=0;
}
}

int max2=MaxChes(b,a,x,n);
if(max t++;
}
cout< delete []x;
for(i=0;i delete[]a[i];
delete[]a;
for(i=0;i delete[]b[i];
delete []b;
return 0;
}



5、算法分析:
算法的時間主要在判斷是否可以放入戰車和產生隨機位置上,若重復的次數為K,則時間復雜度為O(Kn2)。

轉載于:https://www.cnblogs.com/longay/archive/2011/10/16/2214096.html

總結

以上是生活随笔為你收集整理的转:经典ACM算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。