蓝桥杯算法训练题解

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 si;
typedef vector vi;
typedef map 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 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 si;
typedef vector vi;
typedef map 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 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 si;
typedef vector vi;
typedef map 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 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 si;
typedef vector vi;
typedef map mii;
typedef long long ll;
typedef unsigned long long ull;

const double esp = 1e-5;

#define N 100010

int dp[N][2], n;
vector 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 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 si;
typedef vector vi;
typedef map 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 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 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 si;
typedef vector vi;
typedef map mii;
typedef pair 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 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, greater > 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 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 si;
typedef vector vi;
typedef map 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。
*/


你可能感兴趣的:(c/c++,蓝桥杯,简单题)