算法實驗考試複習

遞歸與分支


內部收益率

題目描述

在這裏插入圖片描述

輸入

在這裏插入圖片描述

輸出

對於每組數據,輸出僅一行,即項目的IRR,四捨五入保留小數點後兩位。

樣例輸入
1
-1 2
2
-8 6 9
0
樣例輸出
1.00
0.50
代碼
#include<iostream>
#include <iomanip>
using namespace std;
int main()
{
	int n, f, CF[12];
	while (cin>>n && n != 0)
	{
		cin >> f;
		double mid, high = 10000, low = -1, r, k, sum;
		for (int i = 0; i < n; i++)
			cin >> CF[i];
		while (high - low > 1.0e-6)    
		{
			mid = (high + low) / 2; 
			k = 1;
			sum = 0;
			for (int j = 0; j < n; j++)
			{
				k *= (1.0 / (1 + mid)); 
				sum += CF[j] * k;  
			}
			if (sum + f > 0)
				low = mid;
			else
				high = mid;
		}
		cout << fixed << setprecision(2) << mid << endl;
	}
	return 0;
}

問題 J: 奶牛的聚會

題目描述

農曆新年馬上就要到了,奶牛們計劃舉辦一次聚會慶祝新年的到來。但是,奶牛們並不喜歡走太遠的路,這會給他們的聚會帶來消極情緒,當一頭奶牛的消極指數爲Wi,他參加聚會所需行走的距離爲si,那麼他就會給聚會帶來Si3*Wi的消極情緒。所有奶牛所在位置都在一條直線上,已知所有奶牛的座標和消極指數,求如何確定聚會地點,使得所有奶牛給聚會帶來的消極情緒之和最小,輸出消極情緒之和的最小值。

輸入

第一行包含一個整數 Ca(Ca<=20) ,表示有 Ca 組測試數據。

對於每組測試數據:第一行包含一個整數n(1<=n<=50000) ,表示奶牛的數量。接下來 n 行每行包含兩個浮點數Si和wi (-106<=Si<=106, 0<Wi<15)。

輸出

對於每組測試數據,輸出 “Case #c: ans” ,其中c表示測試數據編號,ans表示消極情緒之和的最小值,結果四捨五入爲一個整數。

樣例輸入
1
5
0.9 2
1.4 4
3.1 1
6.2 1
8.3 2
樣例輸出
Case #1: 300
思路

三分查找法確定消極情緒之和最小的位置,結合註釋應該不難理解。

代碼
#include <iostream>
#include <math.h>
using namespace std;
typedef long long ll;
const int maxn = 50010;
double Si[maxn], wi[maxn];
int n, Ca;
//計算聚會位置在pos時的消極情緒之和
double ans(double pos)
{
    double sum = 0;
    for (int i = 0; i < n; i++)
    {
        //計算每個奶牛距離聚會位置的距離(非負)
        double dist = Si[i] - pos;
        if (dist < 0)
            dist = -dist;
        sum += pow(dist, 3) * wi[i];
    }
    return sum;
}
int main()
{
    cin >> Ca;
    for (int i = 1; i <= Ca; i++)
    {
        cin >> n;
        for (int j = 0; j < n; j++)
        {
            cin >> Si[j] >> wi[j];
        }
        //找到座標位置最小的奶牛
        double low = Si[0];
        for (int k = 0; k < n; k++)
            if (Si[k] < low)
                low = Si[k];
        //找到座標位置最小的奶牛
        double high = Si[0];
        for (int l = 0; l < n; l++)
            if (Si[l] > high)
                high = Si[l];
        //三分查找法確定最終位置
        while (high - low > 1e-7)
        {
            double m1 = (high + low) / 2.0;
            double m2 = (m1 + high) / 2.0;
            if (ans(m1) > ans(m2))
                low = m1;
            else
                high = m2;
        }
        cout << "Case #" << i << ": " << ll(ans(low) + 0.5) << endl;
    }
    return 0;
}

問題 E: 光合作用

題目描述

蒜頭是個愛學習的孩子,他總喜歡在生活中做一些小實驗,這次蒜頭想研究一下光合作用。蒜頭的實驗材料有如下幾樣:神奇的種子,普通的紙箱和一些光源。一開始,蒜頭將種子均勻的種在了箱子底部,你可以將其看成 X 軸,種子的位置爲 X 軸上的點。然後蒜頭用紙板將箱子蓋住,並在紙板上安裝了一些光源(具體見圖,頂上的爲光源,光源兩邊與頂部的夾角都爲45度,黃色部分爲光照,綠色的爲植物。)。神奇的種子會在有光的情況下一直向上生長直到沒光爲止。現在蒜頭想知道當實驗結束時每顆種子的高度是多少?

輸入

第一行輸入一個整數 T,表示測試數據的組數。

每組數據的第一行是三個整數 n,m,h(1<=n,m<=1e5,0<=m<=1e5,1<=h<=1e4),n表示種子數(編號爲1,2…n),m表示光源數,h 表示箱子的高度。

接下來m行,每行一個整數Xi表示第i個光源在頂部的位置。

輸出

對於每組測試數據,請輸出n行,每行一個數表示第i顆種子的最終高度。

樣例輸入
2
7 1 2
4
4 4 1
1
2
3
4
樣例輸出
0
0
1
2
1
0
0
1
1
1
1
思路

二分法

代碼
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1e5 + 5;
int x[maxn];
int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		int n, m, h;
		cin >> n >> m >> h;
		for (int i = 1; i <= m; i++)
		{
			cin >> x[i];
		}
		sort(x + 1, x + m + 1);
		for (int i = 1; i <= n; i++)
		{
			int ans = 0;
			int cnt = lower_bound(x + 1, x + m + 1, i) - x;
			if (cnt == 1 && m != 0)
			{
				ans = max(ans, h - x[cnt] + i);
			}
			else if (cnt == m + 1 && m != 0)
			{
				ans = max(ans, h - i + x[cnt - 1]);
			}
			else if (m != 0)
			{
				ans = max(0, max(h - i + x[cnt - 1], h - x[cnt] + i));
			}
			cout << ans << endl;
		}
	}
	return 0;
}

動態規劃


跳臺階

題目描述

一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

輸入

多組測試樣例。每組測試樣例包含一個整數n。(1<=n<=100)

輸出

每組測試樣例輸出一行,表示青蛙跳上n級臺階的跳法數量.

所得到的結果模1000000007

樣例輸入
3
4
樣例輸出
3
5
思路

可以有多種方法,我這裏選用動態規劃。

dp[i]表示i階臺階的跳法有多少種,一次只能跳一階或兩階。

已知dp[1]=1,dp[2]=2,所以dp[i]=dp[i-1]+dp[i-2],表示:
到達i階 = (最後一步跳一階到達i)+ (最後一步跳兩階到達i)

代碼
#include <iostream>
using namespace std;
typedef long long ll;
int dp[105];
int climb(int n)
{
	if (n == 1)
		return 1;
	dp[1] = 1;
	dp[2] = 2;
	for (int i = 3; i <= n; i++)
	{
		dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007;
	}
	return dp[n];
}
int main()
{
	int n;
	while (cin >> n)
	{
		cout << climb(n) << endl;
	}
	return 0;
}

最長公共子序列

題目描述

給你一個序列X和另一個序列Z,當Z中的所有元素都在X中存在,並且在X中的下標順序是嚴格遞增的,那麼就把Z叫做X的子序列。
例如:Z=<a,b,f,c>是序列X=<a,b,c,f,b,c>的一個子序列,Z中的元素在X中的下標序列爲<1,2,4,6>。
現給你兩個序列X和Y,請問它們的最長公共子序列的長度是多少?

輸入

輸入包含多組測試數據。每組輸入佔一行,爲兩個字符串,由若干個空格分隔。每個字符串的長度不超過100。

輸出

對於每組輸入,輸出兩個字符串的最長公共子序列的長度。

樣例輸入
abcfbc abfcab
programming contest 
abcd mnp
樣例輸出
4
2
0
思路

在這裏插入圖片描述
在這裏插入圖片描述

代碼
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN = 105;
int main()
{
	char x[MAXN], y[MAXN];
	int c[MAXN][MAXN] = { 0 };
	while (cin >> x >> y)
	{
		int m = strlen(x);
		int n = strlen(y);
		for (int i = 1; i <= m; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				if (x[i-1] == y[j-1])
				{
					c[i][j] = c[i - 1][j - 1] + 1;
				}
				else if (c[i - 1][j] > c[i][j - 1])
				{
					c[i][j] = c[i - 1][j];
				}
				else
				{
					c[i][j] = c[i][j - 1];
				}
			}
		}
		cout << c[m][n] << endl;
	}
	return 0;
}

矩陣連乘

題目描述

給定n個矩陣{A1,A2,…,An},及m個矩陣連乘的表達式,判斷每個矩陣連乘表達式是否滿足矩陣乘法法則,如果滿足,則計算矩陣的最小連乘次數,如果不滿足輸出“MengMengDa“。

輸入

輸入數據由多組數據組成(不超過10組樣例)。每組數據格式如下:
第一行是2個整數n (1≤n≤26)和m(1≤m≤3),表示矩陣的個數。
接下來n行,每行有一個大寫字母,表示矩陣的名字,後面有兩個整數r和c,分別表示該矩陣的行數和列數,其中1<r, c<100。
第n+1行到第n+m行,每行是一個矩陣連乘的表達式(2<=矩陣個數<=100)。

輸出

對於每個矩陣連乘表達式,如果運算不滿足矩陣乘法法則的情況(即左矩陣列數與右矩陣的行數不同),則輸出“MengMengDa”,否則輸出最小矩陣連乘次數。

數據保證結果不超過1e9。

樣例輸入
3 2
A 10 100
B 5 50
C 100 5
ACB
ABC
樣例輸出
7500
MengMengDa
思路

矩陣連乘遞歸式:
在這裏插入圖片描述
矩陣連乘的部分不是很複雜,一個函數即可完成,主要的代碼量集中在數據的輸入和轉換上。

  • 用結構體Matrix表示矩陣的行和列
  • 再map存儲名字和對應的矩陣
  • 將輸入的矩陣轉換爲p數組的同時,判斷每相鄰的兩個矩陣,前一矩陣的行數是否等於後一矩陣的列數,若存在不等的情況,直接輸出MengMengda;否則將其列數存入p數組內
代碼
#include <iostream>
#include <map>
#include <string>
using namespace std;
const int maxn = 105;
int m[maxn][maxn];
//用Matrix表示一個矩陣
void MatrixLength(int *p, int n)
{
    //置對角線爲0
    for (int i = 1; i <= n; i++)
        m[i][i] = 0;
    //該循環總共n-1次
    for (int r = 2; r <= n; r++)
    {
        //n-r+1表示每一斜列的個數,i表示行號
        for (int i = 1; i <= n - r + 1; i++)
        {
            int j = r - 1 + i;
            //在第一個矩陣後面加斷點
            m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];
            for (int k = i + 1; k < j; k++)
            {
                //每次將斷點的位置向後移位一次,並更新
                int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
                if (t < m[i][j])
                {
                    m[i][j] = t;
                }
            }
        }
    }
    cout << m[1][n] << endl;
}
struct Matrix
{
    int r;
    int c;
};

int main()
{
    int n, l;
    while(cin>>n>>l)
    {
        map<char, Matrix> matrix;
        while(n--)
        {
            char name;
            int row, col;
            cin >> name >> row >> col;
            Matrix temp;
            temp.r = row;
            temp.c = col;
            matrix[name] = temp;
        }
        string Multi;
        int p[105];
        while(l--)
        {
            cin >> Multi;
            int len = Multi.length();
            int flag = 0;
            p[0] = matrix[Multi[0]].r;
            p[1] = matrix[Multi[0]].c;
            for (int j = 1; j < len;j++)
            {
                if(matrix[Multi[j-1]].c!=matrix[Multi[j]].r)
                {
                    flag = 1;
                    break;
                }
                p[j + 1] = matrix[Multi[j]].c;
            }
            if(flag==1)
                cout << "MengMengDa" << endl;
            else
            {
                MatrixLength(p, len);
            }
            
        }
    }
    return 0;
}


01揹包問題

題目描述

已知有N種物品和一個可容納C重量的揹包。每種物品i的重量爲Wi,價值爲Pi。那麼,採用怎樣的裝包方法纔會使裝入揹包物品的總價值最大。

輸入

包含多組測試數據。第一行爲一個整數T(1<=T<=10),代表測試數據個數。

接下來有T組測試數據。每組測試數據第一行爲揹包的重量C(C<10000)和物品個數N(N<1000)。接下來的N行分別爲物品的重量cost(1<=cost<=100)和價值val(1<=val<=3000000)。(注意:結果可能超過int範圍)

輸出

對每組測試數據,輸出其對應的所裝物品的最大價值。

樣例輸入
1
10 5
2 6
2 3
6 5
5 4
4 6
樣例輸出
15
思路

m(i,j)表示揹包容量爲j,可選物品爲i,i+1,...,n時的最優解。
在這裏插入圖片描述
這一題不知道爲什麼用cin\cout就會報錯,用scanfprintf則正確…

代碼
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn = 1005;
ll m[maxn][maxn];
int main()
{
    int t, n;
    ll c, w[maxn], v[maxn];
    scanf("%d", &t);
    //cin >> t;
    while (t--)
    {
        scanf("%lld %d", &c, &n);
        //cin >> c >> n;
        for (int i = 1; i <= n; i++)
        {
            scanf("%lld %lld", &w[i], &v[i]);
            //cin >> w[i] >> v[i];
        }
        int i,j,jMax = min(w[n] - 1, c);
        for (int j = 0; j <= jMax; j++)
            m[n][j] = 0;
        for (int j = w[n]; j <= c; j++)
            m[n][j] = v[n];
        for (int i = n - 1; i > 1; i--)
        {
            jMax = min(w[i] - 1, c);
            for (int j = 0; j <= jMax; j++)
                m[i][j] = m[i + 1][j];
            for (int j = w[i]; j <= c; j++)
                m[i][j] = max(m[i + 1][j], m[i + 1][j - w[i]] + v[i]);
        }
        m[1][c] = m[2][c];
        if (c >= w[1])
            m[1][c] = max(m[1][c], m[2][c - w[1]] + v[1]);
        printf("%lld\n", m[1][c]);
        //cout << m[1][c] << endl;
    }
    return 0;
}

最大子段和

題目描述

給定n個整數組成的序列a1,a2,…an, 求子段和ai+ai+1+…+aj(子段可爲空集)的最大值。

輸入

包含多組測試數據。第一行爲一個整數T(1<=T<=20),代表測試數據個數。

每組測試數據第一行爲一個整數n,代表有n個整數(1<=n<=10000)。

接下來一行有n個數x(-1000<=x<=1000)。

輸出

輸出其對應的最大子段和。

樣例輸入
1
6
2 -11 4 13 -1 2
樣例輸出
18
提示

子段可爲空集,答案爲0

思路

在這裏插入圖片描述
遞歸方程如下,b[j]表示1到j的最大字段和:
在這裏插入圖片描述

代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int a[maxn],b[maxn];
int main()
{
    int t, n;
    cin >> t;
    while(t--)
    {
        cin >> n;
        int sum = 0;
        for (int i = 1; i <= n;i++)
        {
            cin >> a[i];
        }
        for (int i = 1; i <= n;i++)
        {
            if(b[i-1]>0)
                b[i] = b[i - 1] + a[i];
            else
                b[i] = a[i];
            sum = max(sum, b[i]);
        }
        cout << sum << endl;
    }
    return 0;
}


節食的限制

題目描述

Bessie像她的諸多姊妹一樣,因為從Farmer John的草地吃了太多美味的草而長出了太多的贅肉。所以FJ將她置於一個及其嚴格的節食計劃之中。她每天不能喫多過H(5<=H<=45000)公斤的乾草。Bessie只能喫一整綑乾草;當她開始喫一綑乾草的之後就再也停不下來了。她有一個完整的N(1<=n<=50)綑可以給她當作晚餐的乾草的清單。她自然想要儘量喫到更多的乾草。很自然地,每綑乾草只能被喫一次(即使在列表中相同的重量可能出現2次,但是這表示的是兩綑乾草,其中每綑乾草最多隻能被喫掉一次)。 給定一個列表表示每綑乾草的重量Si(1<=Si<=H),求Bessie不超過節食的限制的前提下可以喫掉多少乾草(注意一旦她開始喫一綑乾草就會把那一綑乾草全部喫完)。

輸入

第一行:兩個由空格隔開的整數:H和N, 第2到N+1行:第i+1行是一個單獨的整數,表示第i綑乾草的重量Si。

輸出

一個單獨的整數表示Bessie在限制範圍內最多可以喫多少公斤的乾草。

樣例輸入
56 4
15
19
20
21
樣例輸出
56
思路

重量和價值相等的0-1揹包問題。

代碼
#include <bits/stdc++.h>
using namespace std;
int dp[55][45005];
int w[55];
int main()
{
    int c, n;
    cin >> c >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i];
    }
    int jMax = min(w[n] - 1, c);
    for (int j = 0; j <= jMax; j++)
        dp[n][j] = 0;
    for (int j = w[n]; j <= c; j++)
        dp[n][j] = w[n];
    for (int i = n - 1; i > 1; i--)
    {
        jMax = min(w[i] - 1, c);
        for (int j = 0; j <= jMax; j++)
            dp[i][j] = dp[i + 1][j];
        for (int j = w[i]; j <= c; j++)
            dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - w[i]] + w[i]);
    }
    dp[1][c] = dp[2][c];
    if (c >= dp[1][c])
        dp[1][c] = max(dp[1][c], dp[2][c - w[1]] + w[1]);
    cout << dp[1][c] << endl;
    return 0;
}


汽車費用

題目描述

一個特別的單行街道在每公里處有一個汽車站。顧客根據他們乘坐汽車的公里使來付費。例如下表就是一個費用的單子。沒有一輛車子行駛超過10公里,一個顧客打算行駛n公里(1<=n<100),它可以通過無限次的換車來完成旅程。最後要求費用最少。

輸入

第一行十個整數分別表示行走1到10公里的費用(<=500)。注意這些數並無實際的經濟意義,即行駛10公里費用可能比行駛一公里少。第二行一個整數n表示,旅客的總路程數。

輸出

僅一個整數表示最少費用。

樣例輸入
12 21 31 40 49 58 69 79 90 101
15
樣例輸出
147
思路

動態規劃,依次計算出行走i公里需要的最少費用。

計算行走i公里最少費用時,用j遍歷[1~i-1],依次比較price[i]price[j]+price[i-j]的值,遍歷完成後,price[i]爲最少費用。

例如,price[4] = min(price[4], price[1]+price[3], price[2]+price[2], price[3]+price[1])

代碼
#include <bits/stdc++.h>
using namespace std;
int price[105];
int dist;
const int inf = 0x3f3f3f3f;
int main()
{
	for (int i = 1; i <= 10; i++)
	{
		cin >> price[i];
	}
	cin >> dist;
	for (int i = 11; i <= dist; i++)
	{
		price[i] = inf;
	}
	for (int i = 1; i <= dist; i++)
	{
		for (int j = 1; j < i; j++)
		{
			price[i] = min(price[i], price[j] + price[i - j]);
		}
	}
	cout << price[dist] << endl;
	return 0;
}

求數組的最長遞減子序列

題目描述

給定一個整數序列,輸出它的最長遞減(注意不是“不遞增”)子序列。

輸入

輸入包括兩行,第一行包括一個正整數N(N<=1000),表示輸入的整數序列的長度。第二行包括用空格分隔開的N個整數,整數範圍區間爲[-30000,30000]。

輸出

輸出最長遞減子序列,數字之間有一個空格。

樣例輸入
8
9 4 3 2 5 4 3 2
樣例輸出
9 5 4 3 2
思路

dp[i]存儲[i~n]區間的最長遞減子序列,雙重循環從後向前遍歷,內層循環從[i+1~n],找出最大值小於i的最長遞減子序列的長度;外層循環從[n-2~0],將內層循環找到的最大值加一,更新所有的dp[i]

輸出最優解時,因爲剛纔是從後向前遍歷的,所以所有最大遞增子序列均在maxpos之後。遍歷[maxpos~n],尋找最大遞減子序列的下一元素時,只需要判斷其值是否小於上一元素且它的最大遞減子序列是否比上一元素的小1,即可。

解答
#include <iostream>
using namespace std;
const int maxn = 1005;
int a[maxn];
//dp[i]存儲[i~end]區間的最長遞減子序列
int dp[maxn];
int main()
{
    int n, maxval = 1, maxpos = 0;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        dp[i] = 1;
    }
    for (int i = n - 2; i >= 0; i--)
    {
        int max = 0; //保存從[i+1~end]區間內的最長遞減子序列
        for (int j = i + 1; j < n; j++)
        {
            //找出[i+1~end]區間中最大值比i小的最長遞減子序列
            if (a[i] > a[j])
            {
                max = dp[j] > max ? dp[j] : max;
            }
        }
        //i的最長遞減子序列=[i+1~end]區間的最長遞減子序列+1
        dp[i] = max + 1;
        if (dp[i] > maxval)
        {   
            //存儲最優值
            maxval = dp[i];
            //存儲最優值的位置
            maxpos = i;
        }
    }
    cout << a[maxpos] << " ";
    //從後向前遍歷的,因此所有最大遞增子序列均在maxpos之後
    for (int i = maxpos + 1; i < n; i++)
    {
        if (dp[maxpos] == dp[i] + 1 && a[maxpos] > a[i])
        {
            cout << a[i] << " ";
            maxpos = i;
        }
    }
    return 0;
}

貪心


問題 B: 哈夫曼編碼

題目描述

給定一隻含有小寫字母的字符串;輸出其哈夫曼編碼的長度

輸入

第一行一個整數T,代表樣例的個數,接下來T行,每行一個字符串,0<T<=2000,字符串長度0<L<=1500.

輸出

對於每個字符串,輸出其哈夫曼編碼長度

樣例輸入
3
hrvsh
lcxeasexdphiopd
mntflolfbtbpplahqolqykrqdnwdoq
樣例輸出
10
51
115
思路

哈夫曼編碼的思路不難,主要講一下優先級隊列的使用。

在STL裏有這個priority_queue,實現優先隊列的結構,在優先隊列中,優先級高的元素先出隊列。

模板聲明(3個參數):priority_queue<Type, Container, Functional>

  • Type 爲數據類型
  • Container 爲保存數據的容器, 必須是用數組實現的容器,比如 vectordeque 但不能用 list。默認用的是 vector
  • Functional 爲元素比較方式,默認用 operator< , 即隊頭元素最大。

所以如果後面倆個參數缺省的話,優先隊列就是大頂堆,隊頭元素最大。

如果要用到小頂堆,則一般要把模板的三個參數都帶進去。STL裏面定義了一個仿函數 greater<>,對於基本類型可以用這個仿函數聲明小頂堆priority_queue<int, vector<int>, greater<int> >q;,即隊頭元素最小。對於自定義類型,則必須自己重載 operator< 或者自己寫比較函數。

優先級隊列的幾個操作:

  • empty() 如果優先隊列爲空,則返回真
  • pop() 刪除第一個元素
  • push() 插入一個元素
  • size() 返回優先隊列中擁有的元素的個數
  • top() 返回優先隊列中有最高優先級的元素
解答
#include <iostream>
#include<queue>
#include<string.h>
using namespace std;
int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        char s[1505];
        //存放每個字母的頻率
        int n[26] = {0};
        cin >> s;
        for (int i = 0; i < strlen(s); i++)
        {
            n[s[i] - 'a']++;
        }
        //建立一個優先級隊列
        priority_queue<int, vector<int>, greater<int>> q;
        for (int i = 0; i < 26; i++)
        {
            if (n[i] > 0)
                q.push(n[i]);
        }
        int sum = 0;
        //不斷更新優先級隊列
        while (q.size() >= 2)
        {
            int a = q.top();
            q.pop();
            int b = q.top();
            q.pop();
            int temp = a + b;
            q.push(temp);
            sum += temp;
        }
        cout << sum << endl;
    }
    return 0;
}

問題 D: Homework

題目描述

臨近開學了,大家都忙着收拾行李準 備返校,但 I_Love_C 卻不爲此擔心! 因爲他的心思全在暑假作業上:目前爲止還未開動。

暑假作業是很多張試卷,我們這些從試卷裏爬出來的人都知道,卷子上的題目有選擇題、填空題、簡答題、證明題等。而做選擇題的好處就在於工作量很少,但又因爲選擇題題目都普遍很長。如果有 5 張試卷,其中 4 張是選擇題,最後一張是填空題,很明顯做最後一張所花的時間要比前 4 張長很多。但如果你只做了選擇題,雖然工作量很少,但表面上看起來也已經做了4/5的作業了。

I_Love_C決定就用這樣的方法來矇混過關,他統計出了做完每一張試卷所需的時間以及它做完後能得到的價值(按上面的原理,選擇題越多價值當然就越高咯)。

現在就請你幫他安排一下,用他僅剩的一點時間來做最有價值的作業。

輸入

測試數據包括多組。每組測試數據以兩個整數 M,N(1<M<20,1<N<10000) 開頭,分別表示試卷的數目和 I_Love_C 剩下的時間。接下來有 M 行,每行包括兩個整數 T,V(1<T<N,1<V<10000)分別表示做完這張試卷所需的時間以及做完後能得到的價值,輸入以 0 0 結束。

輸出

對應每組測試數據輸出 I_Love_C 能獲得的最大價值。保留小數點 2 位

提示:float 的精度可能不夠,你應該使用 double 類型。

樣例輸入
4 20
4 10
5 22
10 3
1 2
0 0
樣例輸出
37.00
思路

0-1揹包類的貪心算法,計算物品性價比,按性價比從大到小排序,優先裝入性價比大的,直到容量滿爲止。

解答
#include <iostream>
#include <algorithm>
#include <iomanip>
using namespace std;
const int maxn = 25;
struct paper
{
    double t, v;
    double cost; //性價比
} paper[maxn];
bool cmp(struct paper a, struct paper b)
{
    return a.cost > b.cost;
}
int main()
{
    int n, c;
    while (cin >> n >> c)
    {
        if (n == 0 && c == 0)
            break;
        for (int i = 0; i < n; i++)
        {
            cin >> paper[i].t >> paper[i].v;
            //計算每張試卷的性價比
            paper[i].cost = paper[i].v / paper[i].t;
        }
        //按性價比排序
        sort(paper, paper + n, cmp);
        double max = 0, time = c;
        for (int i = 0; i < n; i++)
        {
            if (paper[i].t <= time)
            {
                max += paper[i].v;
                time -= paper[i].t;
            }
            else
            {
                //得到部分價值(時間*性價比)
                max += time * paper[i].cost;
                break;
            }
        }
        //printf("%.2f\n", max);
        cout << setiosflags(ios::fixed) << setprecision(2) << max << endl;
    }
    return 0;
}

回溯


圖的m着色問題

題目描述

給定無向連通圖G和m種不同的顏色,用這些顏色給圖的各個頂點着一種顏色,若某種方案使得圖中每條邊的2個頂點的顏色都不相同,則是一個滿足的方案,找出所有的方案。

輸入

第一行有3個正整數n,k和m,分別表示n個頂點,k條邊,m種顏色
接下來k行,每行2個正整數,表示一條邊的兩個頂點

輸出

所有不同的着色方案數

樣例輸入
5 8 4 
1 2
1 3 
1 4
2 3
2 4
2 5
3 4
4 5
樣例輸出
48
代碼
#include <iostream>
using namespace std;
const int maxn = 2e3 + 5;
//n個頂點,k條邊,m種顏色
int n, k, m, res = 0;
int map[maxn][maxn];
int color[maxn];
void dfs(int d)
{
    if (d == n + 1)
    {
        res++;
        return;
    }
    //m種顏色
    for (int i = 1; i <= m; i++)
    {
        int flag = 1;
        //深度優先,若相鄰且有子節點同樣上了i顏色,則剪去
        for (int j = 1; j <= n; j++)
        {
            if (map[d][j] == 1 && color[j] == i)
            {
                flag = 0;
                break;
            }
        }
        if (flag == 1) //可行
        {
            color[d] = i; //上色
            dfs(d + 1);   //遞歸下一節點
            color[d] = 0; //恢復顏色
        }
    }
}
int main()
{
    cin >> n >> k >> m;
    for (int i = 0; i < k; i++)
    {
        int tmp1, tmp2;
        cin >> tmp1 >> tmp2;
        map[tmp1][tmp2] = 1;
        map[tmp2][tmp1] = 1;
    }
    dfs(1);
    cout << res << endl;
    return 0;
}

部分和問題

題目描述

給定n個整數,判斷是否可以從中選擇若干數字,使得他們的和恰好爲k。

輸入

多組測試用例。

對於每組測試用例,第一行一個正整數n,第二行n個整數,第三行一個整數k。

1≤N≤20,輸入整數及k均小於1e8。

輸出

若可以使得和爲k,輸出”Yes”,否則”No”。

樣例輸入
4
1 2 4 7
13
樣例輸出
Yes
思路

每個數有加或不加兩種可能,可以構造一棵二叉樹,深度優先遍歷,終止調節爲和達到要求(返回true),遍歷完全(返回false)。

代碼
#include <iostream>
using namespace std;
const int maxn = 25;
int n, k, d[maxn];
bool dfs(int l, int sum)
{
    if (sum == k)
        return true;
    if (l == n)
        return false;
    return dfs(l + 1, sum) || dfs(l + 1, sum + d[l]);
}
int main()
{
    while (cin >> n)
    {
        for (int i = 0; i < n; i++)
            cin >> d[i];
        cin >> k;
        if (dfs(0, 0))
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

其他


法師康的工人

題目描述

三個法師康的工人每天早上6點到工廠開始到三條產品生產線上組裝桔子手機。第一個工人在200時刻開始(從6點開始計時,以秒作爲單位)在生產線上開始生產,一直到1000時刻。第二個工人,在700時刻開始,在1100時刻結束。第三個工人從1500時刻工作到2100時刻。期間最長至少有一個工人在生產線上工作的連續時間爲900秒(從200時刻到1100時刻),而最長的無人生產的連續時間(從生產開始到生產結束)爲400時刻(1100時刻到1500時刻)。

你的任務是用一個程序衡量N個工人在N條產品線上的工作時間列表(1≤N≤5000,以秒爲單位)。

·最長的至少有一個工人在工作的時間段

·最長的無人工作的時間段(從有人工作開始計)

輸入

輸入第1行爲一個整數N,第2-N+1行每行包括兩個均小於1000000的非負整數數據,表示其中一個工人的生產開始時間與結束時間。

輸出

輸出爲一行,用空格分隔開兩個我們所求的數。

樣例輸入
3
200 1000
700 1100
1500 2100
樣例輸出
900 400
思路
  • 先按開始時間排序,然後對每個人進行遍歷。

  • 如果前人的結束時間小於後人的開始時間,那麼中間這段連續時間是沒有人的,則更新最長的無人工作的時間。

  • 如果前人的結束時間大於後人的開始時間,就比較兩者結束的時間,從前人開始時間到兩者間更晚結束的時間內都至少有一個人在,因此更新最長的至少有一個工人在工作的時間。

  • 不斷更新直到最後一個人。

代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5005;
int b[maxn], e[maxn];
struct data
{
    int s, e;
} worker[maxn];
bool cmp(struct data a, struct data b)
{
    return a.s < b.s;
}
int main()
{
    int n, sb, nb;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> worker[i].s >> worker[i].e;
    }
    sort(worker, worker + n, cmp);
    sb = worker[0].e - worker[0].s;
    nb = 0;
    int j = 0;
    for (int i = 1; i < n; i++)
    {
        if (worker[i].s <= worker[j].e)
        {
            worker[j].e = max(worker[i].e, worker[j].e);
            sb = max(sb, worker[j].e - worker[j].s);
        }
        else
        {
            nb = max(nb, worker[i].s - worker[j].e);
            j = i;
        }
    }
    cout << sb << " " << nb << endl;
    return 0;
}


配對元素

題目描述

給出2個序列A={a[1],a[2],…,a[n]},B={b[1],b[2],…,b[n]},從A、B中各選出n個元素進行一一配對(可以不按照原來在序列中的順序),並使得所有配對元素差的絕對值之和最大。

輸入

輸入的第1行爲1個整數n 第2行包含n個整數,題目中的A序列。 第3行包含n個整數,題目中的B序列。

輸出

一個數,最大配對

3與6配對,2與7配對,5與4配對,6與1配對,絕對值之差和爲14 對於10%的數據,有n≤20; 對於30%的數據,有n≤100; 對於50%的數據,有n≤1000; 對於100%的數據,有n≤10000;a[i],b[i]≤1000。

樣例輸入
4
2 5 6 3
1 4 6 7
樣例輸出
14
思路

將兩個數組分別進行升序和降序排列,然後讓一個數組的最大值與另一個數組的最小值相減,依次類推,然後累和即可。

解答
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10005;
int a[maxn], b[maxn];
int n, sum;
bool cmp(int a, int b)
{
    return a > b;
}
int main()
{
    cin >> n;
    for (int i = 0; i < n;i++)
    {
        cin >> a[i];
    }
    for (int i = 0; i < n;i++)
    {
        cin >> b[i];
    }
    sort(a, a + n);
    sort(b, b + n, cmp);
    for (int i = 0; i < n;i++)
    {
        int temp = abs(b[i] - a[i]);
        sum += temp;
    }
    cout << sum << endl;
    return 0;
}

判斷日期是否符合格式

題目描述

我們知道一年有12個月,每個月最多有31天,年有平年和閏年之分,本題目要求如果輸入一個日期,程序需要判斷用戶輸入的日期是否正確。

提示:測試輸入的三個數字中,年份是正數,月份和日期有可能是負數,程序需要對這兩個數爲負數的情況進行判斷。

輸入

多組測試用例,對每組測試用例:

用戶輸入是三個數字,分別表示年,月和日。 例如 2007 10 21 ,表示2007年10月21日,這個輸入經過判斷是正確的。又例如輸入 1993 11 38 ,這個輸入經過判斷是錯誤的,因爲日期不能超過31天。

輸出

程序的輸出分爲兩種,1或者0。1表示輸入正確,0表示輸入錯誤。

樣例輸入
2011 21 10
樣例輸出
0
思路

閏年判斷:(能被4整除&&不能被100整除)||能被400整除
其他沒啥好說的,開一個大小爲12的數組month[12],存每個月的天數,如果是閏年的話就將month[1]加1

解答
#include <bits/stdc++.h>
using namespace std;
bool Judge(int year)
{
	if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
		return true;
	else
		return false;
}
int main()
{
	int y, m, d;
	while (cin >> y >> m >> d)
	{
		int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
		int flag = 0;
		if (Judge(y))
			month[1]++;
		if (m > 0 && m <= 12 && d > 0 && d <= month[m - 1])
			flag = 1;
		cout << flag << endl;
	}
	return 0;
}

進制轉換

題目描述

輸入一個十進制正整數,然後輸出它所對應的八進制數。

輸入

輸入一個十進制正整數n(1≤n≤106) 。

輸出

輸出n對應的八進制數,輸出在一行。

樣例輸入
10
樣例輸出
12
代碼
#include <iostream>
using namespace std;
int main()
{
    int x, count = 0;
    int r[100000];
    cin >> x;
    while (x != 0)
    {
        r[count++] = x % 8;
        x = x / 8;
    }
    //倒序輸出
    for (int i = count - 1; i >= 0; i--)
    {
        cout << r[i];
    }
    cout << endl;
    return 0;
}


16級考試題目


問題 A: 星空夢想——魯班

題目描述

魯班七號是王者峽谷裏的射手,站擼英雄。戰場上的魯班七號,機制強大的鯊嘴炮,立刻將擋在前路的任何物體轟飛。正如他所說的,“借你們的肉體試驗下新發明的威力”。是的,這就是魯班大師和他的天才機關造物魯班七號。然而,魯班最爲致命的缺點是腿短,跑得慢,一個稍不留神,便會被刺客所擊殺。

既然腿短,那麼就來多多運動吧,跳跳臺階可還行?假設魯班七號一次可以跳上1級臺階,但極限一次只能跳上2級臺階(腿短沒辦法,嚶嚶嚶)。魯班七號現在從0級階梯開始,最終跳上第n級的臺階,求總共有多少種跳法?

輸入

多組測試用例。

第一行輸入包含一個整數T(1<=T<=50),代表測試用例個數。

接下來T行,每行輸入包含一個整數n(1<=n<=50),代表魯班最終跳上了第n級臺階。

輸出

每組測試用例對應一行輸出,每行輸出一個整數ans,代表魯班最終跳上第n級臺階的跳法種數。

樣例輸入
3
3
4
50
樣例輸出
3
5
20365011074
提示

注意結果超過int範圍,請用long long類型存儲ans

思路

思路一:遞歸法+打表

思路二:動態規劃

dp[i]表示跳到第i級臺階的方法數,則dp[1]=1dp[2]=2dp[i]=dp[i-1]+dp[i-2],即到第i級臺階的最後一步可以選擇跳1級,也可以選擇跳2級,而這兩種方法的結果我們已經由動態規劃得到,直接相加即可。

代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 55;
ll dp[maxn];
int main()
{
    int t, n;
    cin >> t;
    while (cin >> n)
    {
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= n;i++)
        {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        cout << dp[n] << endl;
    }
    return 0;
}

問題 B: 午夜歌劇——元歌

題目描述

元歌是王者峽谷裏的刺客。何謂至高機關之美呢?唯有以至高權力的手令太古奇蹟重現人世,方能稱得上啊。

是的,元歌擅長操控,所做傀儡能起到以假亂真的作用,今天元歌的傀儡變成你的初中數學老師,給你出個數學題:給你一個數字x,讓你求出k7、k6、k5、k4、k3、k2、k1、k0(0<=ki<=9),使得以下等式1成立,最後根據等式2求出最終ans值。

等式1:

在這裏插入圖片描述

等式2:

在這裏插入圖片描述

輸入

多組測試用例。

第一行輸入包含一個整數T(1<=T<=1000),代表測試用例個數。

接下來T行,每一行包含一個整數x(1<=x<=1500000)。

輸出

每組測試用例對應一行輸出,每行輸出一個整數ans,代表最終運算結果。

樣例輸入
3
7
1433223
193224
樣例輸出
10
15116331
1433223
提示

測試數據均大於等於1,不用特判0

思路

其實就是10進制轉7進制

代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e8;
void convert(int n)
{
    int c, r;
    r = n % 7;
    c = n / 7;
    if (c > 0)
    {
        convert(c);
        cout << r;
    }
    else
    {
        cout << n;
    }
}
int main()
{
    int t, n;
    cin >> t;
    while (t--)
    {
        cin >> n;
        convert(n);
        cout << endl;
    }
    return 0;
}

聖誕戀歌——貂蟬

題目描述

貂蟬是王者峽谷裏的法師/刺客,貂蟬打法一定要注意配合技能與被動。半肉出裝加上蛇皮走位,往往可以1打5,輕鬆拿下5殺。語花印被動描述爲:技能命中會爲敵人疊加花之印記,疊加滿4層後印記觸發被動,會給自身回覆生命,同時會對周圍敵人造成真實傷害並減速。
我們現在對貂蟬的技能及被動進行簡化如下:每使用1次技能會攻擊1次目標,每攻擊3次目標,會自動額外攻擊1次目標。
現在,貂蟬在遊戲中使用了n次技能,請問總共會給目標帶來多少次攻擊。

輸入

多組測試數據,第一行輸入包含一個整數T,代表測試樣例個數。
接下來T行,每行輸入包含一個整數n(1<=n<=100),代表貂蟬使用了n次技能。

輸出

每組測試用例對應一行輸出,每行輸出一個整數ans,代表貂蟬對目標進行了ans次攻擊。

樣例輸入
6
1
2
3
4
5
81
樣例輸出
1
2
4
5
7
121
提示

沒思路,喝瓶汽水冷靜下?

思路

每次攻擊ans++,攻擊次數n--count來記錄攻擊的次數,每攻擊3次(count==3),則攻擊次數加1(白送一次),同時將count清0,知道所剩攻擊次數爲0,停止。

代碼
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t, n;
    cin >> t;
    while (t--)
    {
        cin >> n;
        int ans = 0, count = 0;
        while (n != 0)
        {
            count++;
            //每3次攻擊,就多一次攻擊
            if (count == 3)
            {
                n++;
                count = 0;
            }
            ans += 1;
            n--;
        }
        cout << ans << endl;
    }
    return 0;
}


問題 D: 海之徵途——孫策

題目描述

孫策是王者峽谷裏的坦克/戰士。大船靠岸,江郡歡呼着迎來了他們的新領袖,人稱江東小霸王的年輕人。遊戲中,孫策的技能長帆破浪,可以駕船衝鋒,可將船撞向敵方單位或者阻擋物,並造成一定的傷害。

現在,有一羣好奇的江郡小朋友想跟着孫策一起出海航行,但孫策的船承載不了所有小朋友,所以孫策決定,儘可能帶更多的小朋友出海,現在請你幫孫策謀一個策略,使得更多的小朋友有機會出海航行。已知的條件是孫策船的最大載重m,以及n個小朋友的體重。

輸入

多組測試用例。
第一行輸入包含一個整數T(1<=T<=1000),代表測試用例個數。

每組測試用例第一行有兩個整數m和n。(0<=m<=1000, 0<=n<=1000),分別代表船的載重重量和小朋友的個數,接下來一行爲n個小朋友的體重。

輸出

每組測試用例對應一行輸出,每行輸出一個整數ans,代表最多能有ans個小朋友跟着一起出海。

樣例輸入
2
10 4
3 5 2 4
20 9
3 5 2 4 6 1 8 5 9
樣例輸出
3
6
提示

1.小朋友的體重可能相同 2.船可以滿載

思路

貪心算法,先對小朋友按體重升序排列,然後體重小的優先上船,直到容量滿爲止。

代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int t, m, n;
int w[maxn];
int main()
{
    cin >> t;
    while (t--)
    {
        cin >> m >> n;
        for (int i = 0; i < n; i++)
        {
            cin >> w[i];
        }
        sort(w, w + n);
        int ans = 0, c = m;
        for (int i = 0; i < n; i++)
        {
            if (w[i] <= c)
            {
                ans++;
                c -= w[i];
            }
        }
        cout << ans << endl;
    }
    return 0;
}


問題 E: 極冰防禦——盾山

題目描述

盾山是王者峽谷裏的輔助,一夫當關、萬夫莫開,一個好的輔助往往可以給團隊帶來極大幫助。

盾山的遊戲中的一個技能爲不動如山:手握一塊由石頭組成的巨盾,張開巨盾砸向地面,將敵人推開,並持續一段時間。

假設盾山最多隻能承受C重量的盾牌,而現在有N個小石頭,每個石頭i的重量爲Wi,防禦值爲Pi。那麼,呆萌的盾山想知道,他從N個小石頭中挑選M個(M<=N)組成他可承受盾牌,最大的防禦值是多少?

輸入

多組測試用例。
第一行輸入包含一個整數T(1<=T<=10),代表測試用例個數。

接下來有T組測試用例。每組測試用例第一行爲盾山承受盾牌的最大重量C(C<10000)和小石頭的個數N(N<1000)。接下來的N行分別爲小石頭的重量Wi(1<=Wi<=100)和防禦值Pi(1<=Pi<=3000000)。

輸出

每組測試用例對應一行輸出,每行輸出一個整數ans,代表可承受盾牌的最大防禦值。

樣例輸入
1
10 5
2 6
2 3
6 5
5 4
4 6
樣例輸出
15
提示

結果可能超過int範圍,請使用long long類型變量

思路

說了這麼多,其實就是經典的01揹包問題。

之前有一道01揹包用cin/cout過不了,只能用scanf/printf,所以到時候看情況吧…

代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10005;
ll dp[maxn][maxn];
ll w[maxn], p[maxn];
ll c, n;
int main()
{
    int t;
    cin >> t;
    //scanf("%d", &t);
    while (t--)
    {
        cin >> c >> n;
        //scanf("%lld %lld", &c, &n);
        for (int i = 1; i <= n; i++)
        {
            cin >> w[i] >> p[i];
            //scanf("%lld %lld", &w[i], &p[i]);
        }
        ll jMax = min(w[n] - 1, c);
        for (int j = 0; j <= jMax; j++)
            dp[n][j] = 0;
        for (int j = w[n]; j <= c; j++)
            dp[n][j] = p[n];
        for (int i = n - 1; i > 0; i--)
        {
            jMax = min(w[i] - 1, c);
            for (int j = 0; j <= jMax; j++)
                dp[i][j] = dp[i + 1][j];
            for (int j = w[i]; j <= c; j++)
                dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - w[i]] + p[i]);
        }
        dp[1][c] = dp[2][c];
        if (c > w[1])
            dp[1][c] = max(dp[2][c], dp[2][c - w[1]] + p[1]);
        cout << dp[1][c] << endl;
        //printf("%lld\n", dp[1][c]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章