藍橋杯算法訓練題解

1.區間k大數查詢
可以先排序,也可以使用快排的思想。
/*************************************************************************
	> File Name: algo_1.cpp
	> Author: gwq
	> Mail: [email protected] 
	> Created Time: 2014年11月08日 星期六 12時58分31秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;

const double esp = 1e-5;

#define N 1010

int num[N], tmp[N];

int findk(int k, int l, int r)
{
	int key = tmp[l];
	int i = l;
	int j = r;
	//當i==j的時候就找到了key在數組中的下標,就是i
	while (i < j) {
		while (i < j && tmp[j] >= key) {
			--j;
		}
		tmp[i] = tmp[j];
		while (i < j && tmp[i] <= key) {
			++i;
		}
		tmp[j] = tmp[i];
	}
	tmp[i] = key;
	if (i == k) {
		return tmp[k];
	} else if (i < k) {
		return findk(k, i + 1, r);
	} else {
		return findk(k, l, r - 1);
	}
}

int main(int argc, char *argv[])
{
	int n, m, l, r, k;
	while (scanf("%d", &n) != EOF) {
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &num[i]);
		}
		scanf("%d", &m);
		for (int i = 0; i < m; ++i) {
			scanf("%d%d%d", &l, &r, &k);
			int cnt = 0;
			for (int j = l; j <= r; ++j) {
				tmp[cnt++] = num[j];
			}
			//這種先排序,再取第k大的複雜度是mnlogn
			//這裏給的數據量小,可以忍受,還有一種方法
			//是利用快排的思想,可以把複雜度降爲mn,單次
			//查詢第k大的複雜度是logn。
			//從小到大排序
			//sort(tmp, tmp + cnt);
			//第k大的下標就是cnt-k
			//printf("%d\n", tmp[cnt - k]);

			//需要尋找下標爲cnt-k的數
			printf("%d\n", findk(cnt - k, 0, cnt - 1));
		}
	}

	return 0;
}

/*
算法訓練 區間k大數查詢  
時間限制:1.0s   內存限制:256.0MB

問題描述
給定一個序列,每次詢問序列中第l個數到第r個數中第K大的數是哪個。
輸入格式
第一行包含一個數n,表示序列長度。
第二行包含n個正整數,表示給定的序列。
第三個包含一個正整數m,表示詢問個數。
接下來m行,每行三個數l,r,K,表示詢問序列從左往右第l個數到第r個數中,從大往小第K大的數是哪個。序列元素從1開始標號。
輸出格式
總共輸出m行,每行一個數,表示詢問的答案。
樣例輸入
5
1 2 3 4 5
2
1 5 2
2 3 2
樣例輸出
4
2
數據規模與約定
對於30%的數據,n,m<=100;
對於100%的數據,n,m<=1000;
保證k<=(r-l+1),序列中的數<=10^6。
*/

2.最大最小公倍數
分爲幾種情況討論就行了。不過因爲藍橋杯網站的問題,只能對60%.
/*************************************************************************
	> File Name: algo_2.cpp
	> Author: gwq
	> Mail: [email protected] 
	> Created Time: 2014年11月08日 星期六 13時26分29秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;

const double esp = 1e-5;

/*
 * 這個在藍橋杯的訓練網站上只得到60分
 * 不知道是什麼原因。
 */
int main(int argc, char *argv[])
{
	ll n;
	while (cin >> n) {
		ll ans;
		if (n <= 2) {
			//處理好特殊情況
			ans = n;
		} else if (n % 2 == 1) {
			//當n爲奇數時,n,n-1,n-2這三個數一定互質
			//相鄰的兩個整數一定是互質的
			ans = n * (n - 1) * (n - 2);
		} else if (n % 3 != 0) {
			//當n爲偶數時,並且n不能被3整除,那麼n,n-1,n-3
			//這3個數一定互質,並且不能找到更大的數了
			ans = n * (n - 1) * (n - 3);
		} else {
			//當n爲偶數,並且能被3整除時,根據上邊的說法,
			//這個3個數互質,並且也不能找到更大的數了
			ans = (n - 1) * (n - 2) * (n - 3);
		}
		cout << ans << endl;
	}

	return 0;
}

/*
算法訓練 最大最小公倍數  
時間限制:1.0s   內存限制:256.0MB
問題描述
已知一個正整數N,問從1~N中任選出三個數,他們的最小公倍數最大可以爲多少。
輸入格式
輸入一個正整數N。
輸出格式
輸出一個整數,表示你找到的最小公倍數。
樣例輸入
9
樣例輸出
504
數據規模與約定
1 <= N <= 10^6。
*/

3.k好數
簡單的數位dp。
/*************************************************************************
	> File Name: algo_3.cpp
	> Author: gwq
	> Mail: [email protected] 
	> Created Time: 2014年11月08日 星期六 20時32分00秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;

const double esp = 1e-5;

#define N 110
#define MOD (1000000007)

//數位dp,dp[i][j]表示i位數最高位是j的符合題意的數的個數
int dp[N][N];

int main(int argc, char *argv[])
{

	int K, L;
	while (scanf("%d%d", &K, &L) != EOF) {
		clr(dp, 0);
		for (int i = 0; i < K; ++i) {
			dp[1][i] = 1;
		}
		for (int i = 2; i <= L; ++i) {
			for (int j = 0; j < K; ++j) {
				for (int k = 0; k < K; ++k) {
					if (k != j - 1 && k != j + 1) {
						dp[i][j] = (dp[i][j] + dp[i - 1][k]) % MOD;
					}
				}
			}
		}
		int ans = 0;
		for (int i = 1; i < K; ++i) {
			ans = (ans + dp[L][i]) % MOD;
		}

		printf("%d\n", ans);
	}

	return 0;
}

/*
算法訓練 K好數  
時間限制:1.0s   內存限制:256.0MB
      
問題描述
如果一個自然數N的K進製表示中任意的相鄰的兩位都不是相鄰的數字,
那麼我們就說這個數是K好數。求L位K進制數中K好數的數目。
例如K = 4,L = 2的時候,所有K好數爲11、13、20、22、30、31、33 共7個。
由於這個數目很大,請你輸出它對1000000007取模後的值。

輸入格式
輸入包含兩個正整數,K和L。

輸出格式
輸出一個整數,表示答案對1000000007取模後的值。
樣例輸入
4 2
樣例輸出
7
數據規模與約定
對於30%的數據,KL <= 10^6;

對於50%的數據,K <= 16, L <= 10;

對於100%的數據,1 <= K,L <= 100。
*/

4.結點選擇
樹形dp,用dp[u][1]表示u結點被選擇後子樹的最大值,用dp[u][0]表示u結點沒被選擇後子樹的最大值。因爲是樹形,所以要使用dfs來遞歸推導。
/*************************************************************************
	> File Name: algo_4.cpp
	> Author: gwq
	> Mail: [email protected] 
	> Created Time: 2015年03月19日 星期四 14時04分19秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;
typedef unsigned long long ull;

const double esp = 1e-5;

#define N 100010

int dp[N][2], n;
vector<int> edge[N];

void dfs(int u, int f)
{
	for (int i = 0; i < sz(edge[u]); ++i) {
		int v = edge[u][i];
		if (f != v) {
			dfs(v, u);
			dp[u][1] += dp[v][0];
			dp[u][0] += max(dp[v][0], dp[v][1]);
		}
	}
}

int main(int argc, char *argv[])
{
	while (scanf("%d", &n) != EOF) {
		clr(dp, 0);
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &dp[i][1]);
		}
		for (int i = 1; i < n; ++i) {
			int u, v;
			scanf("%d%d", &u, &v);
			edge[u].pb(v);
			edge[v].pb(u);
		}
		dfs(1, -1);
		printf("%d\n", dp[1][0]);
	}

	return 0;
}

/*
問題描述
有一棵 n 個節點的樹,樹上每個節點都有一個正整數權值。如果一個點被選擇了,
那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?

輸入格式
第一行包含一個整數 n 。

接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。

接下來一共 n-1 行,每行描述樹上的一條邊。

輸出格式
輸出一個整數,代表選出的點的權值和的最大值。
樣例輸入
5
1 2 3 4 5
1 2
1 3
2 4
2 5
樣例輸出
12
樣例說明
選擇3、4、5號點,權值和爲 3+4+5 = 12 。
數據規模與約定
對於20%的數據, n <= 20。

對於50%的數據, n <= 1000。

對於100%的數據, n <= 100000。

權值均爲不超過1000的正整數。
*/

5.最短路
單源最短路徑,使用dijkstra算法,因爲數據比較大,需要使用堆優化的算法。剛開始使用普通的算法,超時了一部分。
/*************************************************************************
	> File Name: algo_5.cpp
	> Author: gwq
	> Mail: [email protected] 
	> Created Time: 2015年03月19日 星期四 14時58分31秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;
typedef unsigned long long ull;

const double esp = 1e-5;

#define N 20010

struct Node {
	int v, d;
};

int n, m, d[N], vis[N];
vector<Node> edge[N];

// 普通的dijkstra運行超時,得使用堆優化的dijkstra
void dijkstra(void)
{
	clr(d, 0);
	clr(vis, 0);
	int s = 1;
	for (int i = 1; i <= n; ++i) {
		if (i == s) {
			d[i] = 0;
		} else {
			d[i] = INF;
		}
	}
	for (int i = 1; i <= n; ++i) {
		int x;
		int dis = INF;
		for (int j = 1; j <= n; ++j) {
			if (vis[j] == 0 && dis > d[j]) {
				x = j;
				dis = d[j];
			}
		}
		vis[x] = 1;
		// vector的下標是從0開始的
		for (int j = 0; j < sz(edge[x]); ++j) {
			int v = edge[x][j].v;
			d[v] = min(d[v], d[x] + edge[x][j].d);
		}
	}
}

int main(int argc, char *argv[])
{
	while (scanf("%d%d", &n, &m) != EOF) {
		for (int i = 0; i < m; ++i) {
			int u, v, l;
			scanf("%d%d%d", &u, &v, &l);
			Node x;
			x.v = v;
			x.d = l;
			edge[u].pb(x);
		}
		dijkstra();
		for (int i = 2; i <= n; ++i) {
			printf("%d\n", d[i]);
		}
	}

	return 0;
}

/*
問題描述
給定一個n個頂點,m條邊的有向圖(其中某些邊權可能爲負,但保證沒有負環)。
請你計算從1號點到其他點的最短路(頂點從1到n編號)。

輸入格式
第一行兩個整數n, m。

接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度爲l的邊。

輸出格式
共n-1行,第i行表示1號點到i+1號點的最短路。
樣例輸入
3 3
1 2 -1
2 3 -1
3 1 2
樣例輸出
-1
-2
數據規模與約定
對於10%的數據,n = 2,m = 2。

對於30%的數據,n <= 5,m <= 10。

對於100%的數據,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,
保證從任意頂點都能到達其他所有頂點。
*/

/*************************************************************************
	> File Name: algo_5_heap.cpp
	> Author: gwq
	> Mail: [email protected] 
	> Created Time: 2015年03月19日 星期四 14時58分31秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;

const double esp = 1e-5;

#define N 20010

struct Node {
	int v, d;
};

int n, m, d[N], vis[N];
vector<Node> edge[N];

void dijkstra(void)
{
	clr(vis, 0);
	for (int i = 1; i <= n; ++i) {
		if (i == 1) {
			d[i] = 0;
		} else {
			d[i] = INF;
		}
	}
	priority_queue<pii, vector<pii>, greater<pii> > pq;
	pq.push(make_pair(0, 1));
	while (!pq.empty()) {
		pii x = pq.top();
		pq.pop();
		int u = x.second;
		if (vis[u]) {
			continue;
		}
		vis[u] = 1;
		for (int i = 0; i < sz(edge[u]); ++i) {
			int v = edge[u][i].v;
			if (d[v] > d[u] + edge[u][i].d) {
				d[v] = d[u] + edge[u][i].d;
				pq.push(make_pair(d[v], v));
			}
		}
	}
}

int main(int argc, char *argv[])
{
	while (scanf("%d%d", &n, &m) != EOF) {
		for (int i = 0; i < m; ++i) {
			int u, v, l;
			scanf("%d%d%d", &u, &v, &l);
			Node x;
			x.v = v;
			x.d = l;
			edge[u].pb(x);
		}
		dijkstra();
		for (int i = 2; i <= n; ++i) {
			printf("%d\n", d[i]);
		}
	}

	return 0;
}

/*
問題描述
給定一個n個頂點,m條邊的有向圖(其中某些邊權可能爲負,但保證沒有負環)。
請你計算從1號點到其他點的最短路(頂點從1到n編號)。

輸入格式
第一行兩個整數n, m。

接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度爲l的邊。

輸出格式
共n-1行,第i行表示1號點到i+1號點的最短路。
樣例輸入
3 3
1 2 -1
2 3 -1
3 1 2
樣例輸出
-1
-2
數據規模與約定
對於10%的數據,n = 2,m = 2。

對於30%的數據,n <= 5,m <= 10。

對於100%的數據,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,
保證從任意頂點都能到達其他所有頂點。
*/

6.安慰奶牛
每增加一條邊的花費是2 * w + c[u] + c[v],可以以這個爲新的權值,來用最小生成樹算法來得到結果。最後再加上住處的時間就行了,當然這個住處花費的時間應該是最小的。
/*************************************************************************
	> File Name: algo_6.cpp
	> Author: gwq
	> Mail: [email protected] 
	> Created Time: 2015年03月20日 星期五 16時29分52秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;

const double esp = 1e-5;

#define N 100010

int c[N], n, p, m[N];

struct Node {
	int u, v, w;
	void input(void)
	{
		scanf("%d%d%d", &u, &v, &w);
		// 以這個爲新的權值。再加上住處的時間就行了
		w = 2 * w + c[u] + c[v];
	}
}node[N];

bool cmp(Node u, Node v)
{
	return u.w < v.w;
}

int find(int x)
{
	return ((m[x] == x) ? x : (m[x] = find(m[x])));
}

int main(int argc, char *argv[])
{
	while (scanf("%d%d", &n, &p) != EOF) {
		int minn = INF;
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &c[i]);
			minn = min(minn, c[i]);
		}
		for (int i = 0; i < p; ++i) {
			node[i].input();
		}
		sort(node, node + p, cmp);
		for (int i = 1; i <= n; ++i) {
			m[i] = i;
		}
		int ans = 0;
		for (int i = 0;  i < p; ++i) {
			int x = find(node[i].u);
			int y = find(node[i].v);
			if (x != y) {
				m[x] = y;
				ans += node[i].w;
			}
		}
		printf("%d\n", ans + minn);
	}

	return 0;
}

/*
問題描述
Farmer John變得非常懶,他不想再繼續維護供奶牛之間供通行的道路。道路被用來連
接N個牧場,牧場被連續地編號爲1到N。每一個牧場都是一個奶牛的家。FJ計劃除去P條
道路中儘可能多的道路,但是還要保持牧場之間 的連通性。你首先要決定那些道路是
需要保留的N-1條道路。第j條雙向道路連接了牧場Sj和Ej(1 <= Sj <= N; 1 <= Ej <= N;
Sj != Ej),而且走完它需要Lj的時間。沒有兩個牧場是被一條以上的道路所連接。奶牛
們非常傷心,因爲她們的交通系統被削減了。你需要到每一個奶牛的住處去安慰她們。
每次你到達第i個牧場的時候(即使你已經到過),你必須花去Ci的時間和奶牛交談。
你每個晚上都會在同一個牧場(這是供你選擇的)過夜,直到奶牛們都從悲傷中緩過神來。
在早上 起來和晚上回去睡覺的時候,你都需要和在你睡覺的牧場的奶牛交談一次。
這樣你才能完成你的 交談任務。假設Farmer John採納了你的建議,請計算出使所有
奶牛都被安慰的最少時間。

輸入格式
第1行包含兩個整數N和P。

接下來N行,每行包含一個整數Ci。

接下來P行,每行包含三個整數Sj, Ej和Lj。

輸出格式
輸出一個整數, 所需要的總時間(包含和在你所在的牧場的奶牛的兩次談話時間)。
樣例輸入
5 7
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
樣例輸出
176
數據規模與約定
5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。
*/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章