sdut算法分析oj题目整合
A-眾數問題(分治算法A-D)
Description:
給定含有n個元素的多重集合S,每個元素在S中出現的次數稱為該元素的重數。多重集S中重數最大的元素稱為眾數。例如,S={1,2,2,2,3,5}。多重集S的眾數是2,其重數為3。對于給定的由n 個自然數組成的多重集S,計算S的眾數及其重數。如果出現多個眾數,請輸出最小的那個。
Input:
輸入數據的第1行是多重集S中元素個數n(n<1300000);接下來的n行中,每行有一個最多含有5位數字的自然數.
Output:
輸出數據的第1行給出眾數,第2行是重數。
Sample
Input :
6
1
2
2
2
3
5
Output:
2
3
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int main(){int n;cin>>n;map<int,int> mp;for(int i=0;i<n;i++){int s;cin>>s;mp[s]++;}int max_s = -1;int t;for(auto i:mp){if(i.second>max_s){t = i.first;max_s=i.second;}}cout<<t<<endl<<max_s;return 0;
}
B - 整數因子分解問題
Description:
大于1的正整數n可以分解為:n=x1x2…xm。例如,當n=12 時,共有8 種不同的分解式:
12=12;
12=62;
12=43;
12=34;
12=322;
12=26;
12=232;
12=22*3。
對于給定的正整數n,計算n共有多少種不同的分解式。
Input:
輸入數據只有一行,有1個正整數n (1≤n≤2000000000)。
Output:
將計算出的不同的分解式數輸出。
Sample
Input :
12
Output:
8
代碼塊:
#include<bits/stdc++.h>
using namespace std;
long long a[10000000];//注意數據類型,使用map會超時,判斷邊界是1e7
long long zsfj(int x){long long sum=1;if(x<10000000&&a[x]){ //這里的判斷條件不能顛倒順序,否則超時return a[x];}for(int i=2;i*i<=x;i++){if(x%i==0){sum+=zsfj(i);if(i*i!=x){sum+=zsfj(x/i);}}}if(x<10000000)a[x]=sum;return sum;
}
int main(){int n;cin>>n;printf("%lld\n",zsfj(n));return 0;}
C - 順序表應用7:最大子段和之分治遞歸法
Description:
給定n(1<=n<=50000)個整數(可能為負數)組成的序列a[1],a[2],a[3],…,a[n],求該序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。當所給的整數均為負數時定義子段和為0,依此定義,所求的最優值為: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n。 例如,當(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)時,最大子段和為20。
注意:本題目要求用分治遞歸法求解,除了需要輸出最大子段和的值之外,還需要輸出求得該結果所需的遞歸調用總次數。
遞歸調用總次數的獲得,可以參考以下求菲波那切數列的代碼段中全局變量count的用法:
#include
int count=0;
int main()
{
int n,m;
int fib(int n);
scanf(“%d”,&n);
m=fib(n);
printf(“%d %d\n”,m,count);
return 0;
}
int fib(int n)
{
int s;
count++;
if((n1)||(n0)) return 1;
else s=fib(n-1)+fib(n-2);
return s;
}
Input:
第一行輸入整數n(1<=n<=50000),表示整數序列中的數據元素個數;
第二行依次輸入n個整數,對應順序表中存放的每個數據元素值。
Output:
一行輸出兩個整數,之間以空格間隔輸出:
第一個整數為所求的最大子段和;
第二個整數為用分治遞歸法求解最大子段和時,遞歸函數被調用的總次數。
Sample
Input :
6
-2 11 -4 13 -5 -2
Output:
20 11
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int a[100000];
int cnt=0;
int zdfdh(int left,int right){cnt++;if(left>=right){return a[left]>0?a[left]:0;}int mid=(left+right)/2;int rsum = zdfdh(mid+1,right); //int lsum = zdfdh(left,mid); //注意參數的傳遞,此處非(left,mid-1),因為在傳入右側部分時沒有傳入a[mid]而該函數對兩側要求是閉區間的要求所以需要左側包含a[mid]int t=0,s=-1;for(int i=mid;i>=left;i--){t+=a[i];if(t>s){s=t;}}int t1=0,s1=-1;for(int i=mid+1;i<=right;i++){t1+=a[i];if(t1>s1){s1=t1;}}return max(lsum,max(s1+s,rsum));
}
int main(){int n;cin>>n;for(int i=0;i<n;i++){cin>>a[i];}cout<<zdfdh(0,n-1)<<" "<<cnt;return 0;
}
D - 骨牌鋪方格
Description:
在2×n的一個長方形方格中,用一個1× 2的骨牌鋪滿方格,輸入n ,輸出鋪放方案的總數. 例如n=3時,為2× 3方格,骨牌的鋪放方案有三種,如下圖:
Input:
輸入包含一個整數n,表示該測試實例的長方形方格的規格是2×n (0< n<=50)。
Output:
輸出鋪放方案的總數。
Sample
Input :
3
Output:
3
代碼塊:
#include<bits/stdc++.h>
using namespace std;
long long f[1000];//注意數據類型為long long
int main(){f[1]=1;f[2]=2;int n;cin>>n;for(int i=3;i<=n;i++){f[i]=f[i-1]+f[i-2];}cout<<f[n]<<endl;return 0;
}
A - 高數Umaru系列(9)——哈士奇(動態規劃A-E)
Description:
由于高數巨養的喵星人太傲嬌了,要天天吃新鮮貓糧而且還經常欺負高數巨,所以高數巨決定買幾條哈士奇嘗嘗鮮。這天高數巨來到了二手狗市場買哈士奇,高數巨看完了所有的哈士奇,記下了每條哈士奇的價格,并根據對它們的好感程度給它們每只都賦予了一個萌值。高數現在手里有X元,她想通過購買若干條哈士奇來獲得盡可能多的萌值。現在給定高數巨手里的錢X以及N條哈士奇的價格和萌值,求高數巨最多可獲得多少萌值
Input:
多組輸入。
對于每組輸入,第一行有兩個整數N,X(1 < = N < = 100,1 < = X < = 1000),分別表示哈士奇的數量和高數巨的錢數
接下來的N行每行有兩個整數Pi,Mi(1 < = Pi,Mi < = 100),分別表示第i條哈士奇的價格和萌值
Output:
對于每組數據,輸出一個整數,表示高數巨最多可以獲得的萌值,每組輸出占一行
Sample
Input :
2 100
50 20
60 40
3 100
20 55
20 35
90 95
1 10
20 50
Output:
40
95
0
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int dp[1000];
int t[1000],m[1000];
int main(){int n,x;while(~scanf("%d %d",&n,&x)){for(int i=1;i<=n;i++){cin>>t[i]>>m[i];}memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++){for(int j=x;j>=t[i];j--){dp[j]=max(dp[j],dp[j-t[i]]+m[i]);}}cout<<dp[x]<<endl;}return 0;
}
B - 最少硬幣問題
Description:
設有n種不同面值的硬幣,各硬幣的面值存于數組T[1:n]中。現要用這些面值的硬幣來找錢。可以使用的各種面值的硬幣個數存于數組Coins[1:n]中。
對任意錢數0≤m≤20001,設計一個用最少硬幣找錢m的方法。
對于給定的1≤n≤10,硬幣面值數組T和可以使用的各種面值的硬幣個數數組Coins,以及錢數m,0≤m≤20001,計算找錢m的最少硬幣數。
Input:
輸入數據第一行中只有1個整數給出n的值,第2行起每行2個數,分別是T[j]和Coins[j]。最后1行是要找的錢數m。
Output:
輸出數據只有一個整數,表示計算出的最少硬幣數。問題無解時輸出-1。
Sample
Input :
3
1 3
2 3
5 3
18
Output:
5
代碼塊:
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int t[12],c[12];//面值、數量
int dp[20010];
int main()
{int n;cin>>n;for(int i =0; i<n; i++)cin>>t[i]>>c[i];int p;cin>>p;memset(dp,inf,sizeof(dp));//順序1dp[0] =0;//順序2for(int i = 0; i<n; i++)for(int j = 1; j <= c[i]; j++)//錢個數量是從1開始,不可能給0張for(int k = p; k >= t[i]; k--){dp[k]=min(dp[k],dp[k-t[i]]+1);}if(dp[p] >= inf)dp[p] =-1;cout<<dp[p]<<endl;
}
C - 數字三角形問題
Description:
給定一個由n行數字組成的數字三角形如下圖所示。試設計一個算法,計算出從三角形的頂至底的一條路徑,使該路徑經過的數字總和最大。
對于給定的由n行數字組成的數字三角形,計算從三角形的頂至底的路徑經過的數字和的最大值。
Input:
輸入數據的第1行是數字三角形的行數n,1≤n≤100。接下來n行是數字三角形各行中的數字。所有數字在0…99之間。
Output:
輸出數據只有一個整數,表示計算出的最大值。
Sample
Input :
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Output:
30
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int dp[105][105];
int main(){int n;cin>>n;for(int i=1;i<=n;i++){for(int j=1;j<=i;j++){cin>>dp[i][j];}}for(int i=n-1;i>=1;i--){for(int j=1;j<=i;j++){dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]);}}cout<<dp[1][1]<<endl;return 0;
}
D - 最長公共子序列問題
Description:
給定兩個序列 X={x1,x2,…,xm} 和 Y={y1,y2,…,yn},找出X和Y的最長公共子序列。
Input:
輸入數據有多組,每組有兩行 ,每行為一個長度不超過500的字符串(輸入全是大寫英文字母(A,Z)),表示序列X和Y。
Output:
每組輸出一行,表示所求得的最長公共子序列的長度,若不存在公共子序列,則輸出0。
Sample
Input :
ABCBDAB
BDCABA
Output:
4
代碼塊:
#include<bits/stdc++.h>
using namespace std;
char a[505];
char b[505];
int dp[505][505];
int main(){while(~scanf("%s\n%s",a+1,b+1)){memset(dp,0,sizeof(dp));int l1=strlen(a+1);int l2=strlen(b+1);for(int i=1;i<=l1;i++){for(int j=1;j<=l2;j++){if(a[i]==b[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j] = max(dp[i][j-1],dp[i-1][j]);}}}cout<<dp[l1][l2]<<endl;}return 0;
}
E - 石子合并問題
Description:
在一個圓形操場的四周擺放著n堆石子。現要將石子有次序地合并成一堆。規定每次只能選相鄰的2 堆石子合并成新的一堆,并將新的一堆石子數記為該次合并的得分。試設計一個算法,計算出將n堆石子合并成一堆的最小得分和最大得分。
對于給定n堆石子,計算合并成一堆的最小得分和最大得分。
Input:
輸入數據的第1行是正整數n,1≤n≤100,表示有n堆石子。第二行有n個數,分別表示每堆石子的個數。
Output:
輸出數據有兩行,第1行中的數是最小得分,第2行中的數是最大得分。
Sample
Input :
4
4 4 5 9
Output:
43
54
代碼塊:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f
int stone[105];
int dpmin[205][205];//最小
int dpmax[205][205];//最大
int sum[205];
int main()
{int n;scanf("%d",&n);memset(sum,0,sizeof(sum));memset(dpmin,INF,sizeof(dpmin));memset(dpmax,-1,sizeof(dpmax));for(int i =1;i<=n;i++){scanf("%d",&stone[i]);sum[i] = sum[i - 1] + stone[i];dpmin[i][i] = 0;dpmax[i][i] = 0;}for(int i = 1;i<=n;i++){sum[i+n] = sum[i+n-1]+stone[i];//展開的n后面的n-1~1重量dpmin[i+n][i+n] = 0;dpmax[i+n][i+n] = 0;}for(int len = 2;len<=n;len++){//長度還是最大nfor(int j = 1;j+len-1<=2*n-1;j++){//起點枚舉最大到2*n-1,ends<=2*n-1int ends = j+len - 1;for(int i = j;i<ends;i++){//注意!i<ends!!!因為i=ends時,dp[ends+1][ends]是不成立的!dpmin[j][ends] = min(dpmin[j][ends],dpmin[j][i]+dpmin[i+1][ends]+sum[ends]-sum[j-1]);dpmax[j][ends] = max(dpmax[j][ends],dpmax[j][i]+dpmax[i+1][ends]+sum[ends]-sum[j-1]);}}}int ansmin = 0xfffffff;int ansmax = -1;for(int i = 1;i<=n;i++){ansmin = min(ansmin,dpmin[i][i+n-1]);//找1~n,2~n~1,3~n~2....的合并n個堆的中最大和最小的值ansmax = max(ansmax,dpmax[i][i+n-1]);}cout<<ansmin<<endl;cout<<ansmax<<endl;return 0;
}
A - 汽車加油問題(貪心A-F)
Description:
一輛汽車加滿油后可行駛n公里。旅途中有若干個加油站。設計一個有效算法,指出應在哪些加油站停靠加油,使沿途加油次數最少。并證明算法能產生一個最優解。
對于給定的n和k個加油站位置,計算最少加油次數。
Input:
輸入數據的第一行有2 個正整數n和k(n≤5000,k≤1000),表示汽車加滿油后可行駛n公里,且旅途中有k個加油站。接下來的1 行中,有k+1 個整數,表示第k個加油站與第k-1 個加油站之間的距離。第0 個加油站表示出發地,汽車已加滿油。第k+1 個加油站表示目的地。
Output:
將計算出的最少加油次數輸出。如果無法到達目的地,則輸出“No Solution!”。
Sample
Input :
7 7
1 2 3 4 5 1 6 6
Output:
4
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int dis[5005];
int main(){int n,k;cin>>n>>k;for(int i=1;i<=k+1;i++){cin>>dis[i];}//dis[i]代表i與i-1之間的距離dis[1]表示出發地到第一個加油站的距離int y1 = n;int cnt = 0;int i=0;while(i<=k){if(y1>=dis[i+1]){y1-=dis[i+1];i++;}else{if(y1<dis[i+1]){if(n>=dis[i+1]){y1=n-dis[i+1];cnt++;i++;}else{cout<<"No Solution!";return 0;}}}}cout<<cnt<<endl;return 0;
}
B - 多元Huffman編碼問題
Description:
在一個操場的四周擺放著n堆石子。現要將石子有次序地合并成一堆。規定每次至少選2 堆最多選k堆石子合并成新的一堆,合并的費用為新的一堆的石子數。試設計一個算法,計算出將n堆石子合并成一堆的最大總費用和最小總費用。
對于給定n堆石子,計算合并成一堆的最大總費用和最小總費用。
Input:
輸入數據的第1 行有2 個正整數n和k(n≤100000,k≤10000),表示有n堆石子,每次至少選2 堆最多選k堆石子合并。第2 行有n個數(每個數均不超過 100),分別表示每堆石子的個數。
Output:
將計算出的最大總費用和最小總費用輸出,兩個整數之間用空格分開。
Sample
Input :
7 3
45 13 12 16 9 5 22
Output:
593 199
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int main()
{priority_queue<int,vector<int>,greater<int> >q1;//優先隊列(從小到大)priority_queue<int>q2;//優先隊列(默認從大到小)int n,k;cin>>n>>k;for(int i =0; i<n; i++){int x;cin>>x;q1.push(x);q2.push(x);}while(q1.size()%(k-1)!=1)q1.push(0);long long sum_ma =0,sum_mi =0;while(q1.size()>1){long long sum = 0;for(int i=0; i<k; i++){sum+=q1.top();q1.pop();}sum_mi +=sum;q1.push(sum);}while(q2.size()>1){int a =q2.top();q2.pop();int b =q2.top();q2.pop();sum_ma +=a+b;q2.push(a+b);}cout<<sum_ma<<" "<<sum_mi<<endl;
}
C - 裝船問題
Description:
王小二畢業后從事船運規劃工作,吉祥號貨輪的最大載重量為M噸,有10種貨物可以裝船。第i種貨物有wi噸,總價值是pi。王小二的任務是從10種貨物中挑選若干噸上船,在滿足貨物總重量小于等于M的前提下,運走的貨物的價重比最大。
Input:
輸入數據的第一行有一個正整數M(0 < M < 10000),表示所有貨物最大載重量。在接下來的10行中,每行有若干個數(中間用空格分開),第i行表示的是第i種貨物的貨物的總價值pi ,總重量wi。(pi是wi的整數倍,0 < pi , wi < 1000)
Output:
輸出一個整數,表示可以得到的最大價值。
Sample
Input :
100
10 10
20 10
30 10
40 10
50 10
60 10
70 10
80 10
90 10
100 10
Output:
550
代碼塊:
#include<bits/stdc++.h>
using namespace std;
struct yc{int weight;int price;double w_p;
}a[20];
bool cmp(yc a,yc b){return a.w_p>b.w_p;
}
int main(){int m;cin>>m;for(int i=0;i<10;i++){cin>>a[i].price>>a[i].weight;a[i].w_p = a[i].price/a[i].weight;}sort(a,a+10,cmp);int sum=0;for(int i=0;i<10;i++){if(m>=a[i].weight){m-=a[i].weight;sum+=a[i].price;}else{if(m<a[i].weight){sum+=a[i].w_p*m;m=0;}}if(m==0)break;}printf("%d",sum);return 0;
}
D - 活動選擇
Description:
學校的大學生藝術中心周日將面向全校各個學院的學生社團開放,但活動中心同時只能供一個社團活動使用,并且每一個社團活動開始后都不能中斷。現在各個社團都提交了他們使用該中心的活動計劃(即活動的開始時刻和截止時刻)。請設計一個算法來找到一個最佳的分配序列,以能夠在大學生藝術中心安排不沖突的盡可能多的社團活動。
比如有5個活動,開始與截止時刻分別為:
最佳安排序列為:1,4,5。
Input:
第一行輸入活動數目n(0<n<100);
以后輸入n行,分別輸入序號為1到n的活動使用中心的開始時刻a與截止時刻b(a,b為整數且0<=a,b<24,a,b輸入以空格分隔)。
Output:
輸出最佳安排序列所包含的各個活動(按照活動被安排的次序,兩個活動之間用逗號分隔),如果有多個活動安排序列符合要求輸出字典序最小的序列。
Sample
Input :
6
8 10
9 16
11 16
14 15
10 14
7 11
Output:
1,5,4
代碼塊:
#include<bits/stdc++.h>
using namespace std;
struct node{int start;int end;int id;
}a[102];
bool cmp(node a,node b){return a.end==b.end?a.id<b.id:a.end<b.end;
}
int main(){int n;cin>>n;for(int i=0;i<n;i++){cin>>a[i].start>>a[i].end;a[i].id=i+1;}sort(a,a+n,cmp);cout<<a[0].id;int end1=a[0].end;for(int i=1;i<n;i++){if(a[i].start>=end1){cout<<","<<a[i].id;end1 = a[i].end;}}return 0;
}
E - 最優合并問題
Description:
給定k 個排好序的序列s1 , s2,……, sk , 用2 路合并算法將這k 個序列合并成一個序列。假設所采用的2 路合并算法合并2 個長度分別為m和n的序列需要m + n -1次比較。試設計一個算法確定合并這個序列的最優合并順序,使所需的總比較次數最少。
為了進行比較,還需要確定合并這個序列的最差合并順序,使所需的總比較次數最多。
對于給定的k個待合并序列,計算最多比較次數和最少比較次數合并方案。
Input:
輸入數據的第一行有1 個正整數k(k≤1000),表示有k個待合并序列。接下來的1 行中,有k個正整數,表示k個待合并序列的長度。
Output:
輸出兩個整數,中間用空格隔開,表示計算出的最多比較次數和最少比較次數。
Sample
Input :
4
5 12 11 2
Output:
78 52
代碼塊:
#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int>> q1;//小頂堆
priority_queue<int> q2;//大頂堆
int main(){int n;cin>>n;for(int i=0;i<n;i++){int x;cin>>x;q1.push(x);q2.push(x);}int misum=0,masum=0;while(q1.size()!=1){int sum=0;int a=q1.top();q1.pop();int b=q1.top();q1.pop();sum=a+b;misum+=(sum-1);q1.push(sum);sum = 0;a=q2.top();q2.pop();b=q2.top();q2.pop();sum=a+b;q2.push(sum);masum+=(sum-1);}cout<<masum<<" "<<misum<<endl;
}
F - 區間覆蓋問題
Description:
設x1 , x2 ,…… , xn 是實直線上的n 個點。用固定長度的閉區間覆蓋這n 個點,至少需要多少個這樣的固定長度閉區間?
對于給定的實直線上的n個點和閉區間的長度k,設計解此問題的有效算法,計算覆蓋點集的最少區間數,并證明算法的正確性。
Input:
輸入數據的第一行有2 個正整數n和k(n≤10000,k≤100),表示有n個點,且固定長度閉區間的長度為k。接下來的1 行中,有n個整數,表示n個點在實直線上的坐標(可能相同)。
Output:
輸出一個整數,表示計算出的最少區間數輸出。
Sample
Input :
7 3
1 2 3 4 5 -2 6
Output:
3
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int a[100005];
bool cmp(int a,int b){return a<b;
}
int main(){int n,k;cin>>n>>k;for(int i=0;i<n;i++){cin>>a[i];}sort(a,a+n,cmp);int end = a[0]+k;int cnt = 1;for(int i=1;i<n;i++){if(end<a[i]){end=a[i]+k;cnt++;}}cout<<cnt<<endl;return 0;
}
A - 子集和問題(搜索算法A-D)
Description:
子集和問題的一個實例為〈S,t〉。其中,S={ x1 , x2 ,…,xn }是一個正整數的集合,c是一個正整數。子集和問題判定是否存在S的一個子集S1,使得:
試設計一個解子集和問題的回溯法。
對于給定的正整數的集合S={ x1 , x2 ,…,xn }和正整數c,計算S 的一個子集S1,使得:
。
Input:
輸入數據的第1 行有2 個正整數n 和c(n≤10000,c≤10000000),n 表示S 的大小,c是子集和的目標值。接下來的1 行中,有n個正整數,表示集合S中的元素。
Output:
將子集和問題的解輸出。當問題無解時,輸出“No Solution!”。
Sample
Input :
5 10
2 2 6 5 4
Output:
2 2 6
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int a[10005];//數組a用來存放集合S的數據
int n,c,sum;
int flag=0;//設立一個標識符,當有解時flag=1
int ans[10005]={0};//數組ans用來存放Search過程中的中轉數組以及最后的輸出數組
void print(int len)//輸出
{for(int i=0;i<len;i++){if(i==len-1)std::cout<<ans[i]<<std::endl;elsestd::cout<<ans[i]<<" ";}
}
void Search(int x,int sum,int len)//遞歸調用Search函數,x是Search的起始位置
{if(sum>c||flag)//相加的和大于了c表示集合中沒有可以匹配的對象,或者flag=1,有匹配對象,return跳出return ;if(sum==c){print(len);flag=1;return ;}for(int i=x;i<n;i++){if(a[i]+sum<=c){ans[len]=a[i];Search(i+1,sum+a[i],len+1);//遞歸調用}}
}
int main()
{std::cin>>n>>c;sum=0;for(int i=0;i<n;i++){std::cin>>a[i];sum+=a[i];}if(sum<c)//首先判斷所有元素的和是否小于c,小于c的話必然無解std::cout<<"No Solution!"<<std::endl;else{Search(0,0,0);if(!flag)std::cout<<"No Solution!"<<std::endl;}return 0;
}
B - 運動員最佳匹配問題
Description:
羽毛球隊有男女運動員各n 人。給定2 個n×n 矩陣P 和Q。P[i][j]是男運動員i 和女運動員j配對組成混合雙打的男運動員競賽優勢;Q[i][j]是女運動員i和男運動員j配合的女運動員競賽優勢。由于技術配合和心理狀態等各種因素影響,P[i][j]不一定等于Q[j][i]。男運動員i和女運動員j配對組成混合雙打的男女雙方競賽優勢為P[i][j]*Q[j][i]。
設計一個算法,計算男女運動員最佳配對法,使各組男女雙方競賽優勢的總和達到最大。
設計一個算法,對于給定的男女運動員競賽優勢,計算男女運動員最佳配對法,使各組男女雙方競賽優勢的總和達到最大。
Input:
輸入數據的第一行有1 個正整數n (1≤n≤20)。接下來的2n 行,每行n個數。前n行是p,后n行是q。
Output:
將計算出的男女雙方競賽優勢的總和的最大值輸出。
Sample
Input :
3
10 2 3
2 3 4
3 4 5
2 2 2
3 5 3
4 5 1
Output:
52
代碼塊:
#include<bits/stdc++.h>
using namespace std;
int x[25][25],y[25][25];
int maxsum[25];//用來剪枝
int tmp[25][25];
int vis[25];
int sum,Max,n;
void dfs(int x)
{if(x>=n)//都遍歷完,更新Max為當前搜索sum和Max的最大值{Max=max(sum,Max);return ;}int cnt=0;for(int i=x; i<n; i++) //cnt存放從x到n個男生的最大優勢和(有可能女生重復匹配)cnt+=maxsum[i];if(sum+cnt<=Max) return ;//如果Max已經大于當前搜索值sum+congx到n的假設匹配最大優勢和cnt;Max已經足夠大,不需要再繼續搜索for(int i=0; i<n; i++) //搜索男生x和女生i的的最佳優勢(因為vis[i]表示女生是否被匹配過,男女匹配不會重){if(!vis[i]){vis[i]=1;sum+=tmp[x][i];dfs(x+1);sum-=tmp[x][i];vis[i]=0;}}
}
int main()
{cin>>n;for(int i=0; i<n; i++){for(int j=0; j<n; j++){cin>>x[i][j];}}for(int i=0; i<n; i++){for(int j=0; j<n; j++){cin>>y[i][j];}}for(int i=0; i<n; i++){for(int j=0; j<n; j++){tmp[i][j]=x[i][j]*y[j][i];//存放男生i和女生j匹配的優勢maxsum[i]=max(maxsum[i],tmp[i][j]);//存放男生i的最大優勢。但maxsum匹配出來的男女生有可能會有重復,所以要用一個vis標記有沒有被訪問過,在dfs中遍歷}}Max=0;sum=0;memset(vis,0,sizeof(vis));dfs(0);cout<<Max<<endl;return 0;
}
C - 工作分配問題
Description:
設有n件工作分配給n個人。將工作i分配給第j個人所需的費用為 cij。試設計一個算法,為每一個人都分配1 件不同的工作,并使總費用達到最小。
設計一個算法,對于給定的工作費用,計算最佳工作分配方案,使總費用達到最小。
Input:
輸入數據的第一行有1 個正整數n (1≤n≤11)。接下來的n行,每行n個數,表示工作費用。
Output:
將計算出的最小總費用輸出。
Sample
Input :
3
10 2 3
2 3 4
3 4 5
Output:
9
代碼塊:
#include <iostream>
using namespace std;
#define inf 0x3f3f3f3fint n, ans;
int c[25][25];
int vis[25];void dfs(int i, int sum)//i是行號
{if (sum > ans) //剪枝return;if (i == n + 1 && sum < ans){ans = sum;return;}for (int j = 1; j <= n; j++){if (!vis[j])//遍歷第i行 沒有被遍歷過列號j 的元素{vis[j] = 1;dfs(i + 1, sum + c[i][j]);vis[j] = 0;}}
}
int main()
{cin >> n;for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)cin >> c[i][j];ans = inf;dfs(1, 0);cout << ans << endl;return 0;}
D - 整數變換問題
Description:
整數變換問題。關于整數i的變換f和g定義如下:f(i)=3i;
試設計一個算法,對于給定的2 個整數n和m,用最少的f和g變換次數將n變換為m。例如,可以將整數15用4 次變換將它變換為整數4:4=gfgg(15)。當整數n不可能變換為整數m時,算法應如何處理?
對任意給定的整數n和m,計算將整數n變換為整數m所需要的最少變換次數。
Input:
輸入數據的第一行有2 個正整數n和m。n≤100000,m≤1000000000。
Output:
將計算出的最少變換次數以及相應的變換序列輸出。第一行是最少變換次數。第2 行是相應的變換序列。
Sample
Input :
15 4
Output:
4
gfgg
代碼塊:
#include<bits/stdc++.h>
using namespace std;
char s[105];
int len;
int maxsum;
int select(int i,int n,int m)
{if(i==0)return 3*n;elsereturn n/2;
}
int dfs(int step,int n,int m)
{if(step>maxsum)return 0;for(int i=0;i<2;i++){int num=select(i,n,m);if(num==m||dfs(step+1,num,m))//dfs返回1說明匹配到n==m{if(i==0){s[len++]='f';//為什么先走乘法??}else{s[len++]='g';}return 1;}}return 0;
}
int main()
{int n,m;cin>>n>>m;len=0;maxsum=1;while(!dfs(1,n,m))//達到剪枝效果{maxsum++;}cout<<maxsum<<endl;for(int i=0;i<len;i++){cout<<s[i];}cout<<endl;return 0;
}
總結
以上是生活随笔為你收集整理的sdut算法分析oj题目整合的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CF1550E Stringforces
- 下一篇: Spark优化篇:RBO/CBO