北京信息科技大学第十一届程序设计竞赛(重现赛)

A-kotori和糖果

链接:https://ac.nowcoder.com/acm/contest/940/A
来源:牛客网
 

题目描述

kotori共有n块糖果,每块糖果的初始状态是分散的,她想把这些糖果聚在一堆。但她每次只能把两堆糖果合并成一堆。

已知把两堆数量为a和b的糖果聚在一堆的代价是|a-b|。

kotori想知道,她把这n块糖果聚在一堆的最小代价是多少?

思路: 使用优先队列, 存每个元素以及其个数, 如果有相同的能合并的, 则选择合并, 否则取两个最小的合并,

代价即为abs(a.x - b.x)

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
struct node{
	ll x, cnt;
	bool operator<(const node b)const
	{
		return x > b.x;
	}
};
int main(){
	int t;
	cin >> t;
	while (t--)
	{
		ll n;
		cin >> n;
		priority_queue, less>pq;
		pq.push({ 1, n });
		ll ans = 0;
		while (!pq.empty())
		{
			node a = pq.top();
			pq.pop();
			if (a.x == n)
				break;
			if (a.cnt % 2 == 0)  // 相同的合并, 无代价
				pq.push({ a.x * 2, a.cnt / 2 });
			else
				if (a.cnt >= 3) // 如果是奇数, 肯定有单独的某个无法合并。
				{
					pq.push({ a.x * 2, a.cnt / 2 });
					pq.push({ a.x, 1 });
				}
				else   // 取最小的两个合并
				{
					node b = pq.top();
					pq.pop();
					ans += abs(b.x - a.x); // 计算代价
					pq.push({ a.x + b.x, 1 });
					if (b.cnt - 1 != 0)
						pq.push({ b.x, b.cnt - 1 });
				}
		}
		cout << ans << endl;
	}
	return 0;
}

B-kotori和气球

链接:https://ac.nowcoder.com/acm/contest/940/B
来源:牛客网
 

题目描述

kotori最近迷上了摆气球的游戏。她一共有n种气球,每种气球有无数个。她要拿出若干个气球摆成一排。

但是,由于气球被施放了魔法,同样种类的气球如果相邻会发生爆炸,因此若两个相邻的气球种类相同被视为不合法的。

kotori想知道,摆成一排m个一共有多少种不同的方案?

由于该数可能过大,只需要输出其对109取模的结果。

思路: 第一个位置有n种, 接下来每个位置都有n - 1 种排列方式, 注意对109取模;

#include 
#include 
#include 
using namespace std;
typedef long long ll;
ll mod = 109;
int main(){
	ll n, m;
	cin >> n >> m;
	ll sum = n;
	for (ll i = 2; i <= m; i++)
		sum = sum * (n - 1) % mod;
	cout << sum << endl;
	return 0;
}

C-kotori和出道

链接:https://ac.nowcoder.com/acm/contest/940/C
来源:牛客网
 

题目描述

kotori和一些偶像们围成一圈,一共有n个偶像(包括kotori自己),她们的序号顺时针从1到n。现在她们从1号开始进行顺时针报数:1,2,1,2,1……

每个报到2的人出列。直到剩下最后一个人为止。那么这个剩下的人就能被选中发专辑出道啦!

kotori可以暗箱操作决定自己的序号。她很想发专辑,于是向聪明的你求助,初始序号设定为多少才可以呢?

思路: 找规律, n从1开始的答案依次是1 1 3 1 3 5 7 1 3 5 7 9 11 13 15 ... 可以发现规律是按1, 2, 4,8...个二进制数, 所以

找到这个n在哪个二进制数后面, 去掉最高位得到m, 答案是从3起的第m个奇数, 即2 * m + 1。 

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int main(){
	int t;
	cin >> t;
	while (t--)
	{
		ll num = 1;
		cin >> num;
		for (int i = 63; i >= 0; i--)
			if ((1LL << i) & num) // 找到最高位
			{
				num ^= (1LL << i);    // 去掉最高位
				break;
			}
		cout << num * 2 + 1 << endl; // 输出
	}
	return 0;
}

D-kotori和迷宫

链接:https://ac.nowcoder.com/acm/contest/940/D
来源:牛客网
 

题目描述

kotori在一个n*m迷宫里,迷宫的最外层被岩浆淹没,无法涉足,迷宫内有k个出口。kotori只能上下左右四个方向移动。她想知道有多少出口是她能到达的,最近的出口离她有多远?

思路:BFS搜索最短的路径, 并记录搜索过程中, 出现的出口, 到出口则跳过, 不再搜索。

#include 
#include 
#include 
using namespace std;
int dir[4][2] = { 0, 1, 0, -1, 1, 0, -1, 0 };
char map[35][35];
int cnt = 0, ans = 0x3f3f3f3f;
int vis[35][35];
int n, m;
struct node 
{
	int x, y, t;
};
void bfs(int x, int y, int t)
{
	queueq;
	q.push({ x, y, t });
	while (!q.empty())
	{
		int x = q.front().x, y = q.front().y, t = q.front().t;
		q.pop();
		if (vis[x][y])
			continue;
		vis[x][y] = 1;
		if (map[x][y] == 'e') // 统计个数
		{
			ans = min(ans, t);
			cnt++;
			continue;
		}
		for (int i = 0; i < 4; i++)
		{
			int xx = x + dir[i][0];
			int yy = y + dir[i][1];
			if (!vis[xx][yy] && xx <= n && xx >= 1 && yy <= m && yy >= 1 && map[xx][yy] != '*')
			{
				q.push({ xx, yy, t + 1 });
			}
		}
	}
}
int main(){
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		scanf("%s", map[i] + 1);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			if (map[i][j] == 'k')
			{
				bfs(i, j, 0);
				if (cnt == 0)
					cout << -1 << endl;
				else
					cout << cnt << " " << ans << endl;
				return 0;
			}
		}
	return 0;
}

E-kotori和素因子

链接:https://ac.nowcoder.com/acm/contest/940/E
来源:牛客网
 

题目描述

kotori拿到了一些正整数。她决定从每个正整数取出一个素因子。但是,kotori有强迫症,她不允许两个不同的正整数取出相同的素因子。

她想知道,最终所有取出的数的和的最小值是多少?

注:若a%k==0,则称k是a的因子。若一个数有且仅有两个因子,则称其是素数。显然1只有一个因子,不是素数。

思路: 将每个数的素因子都记录下来, 再用DFS排列找到最小值。

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
int n, q;
const int N = 1e3 + 10;
int a[N];
vectorv[20];
int vis[N];
int ans = 0x3f3f3f3f;
bool use[N];
bool is_prime(int k)
{
	int num = sqrt(k);
	for (int i = 2; i <= num; i++)
		if (k % i == 0)
			return false;
	return true;
}
void get_num(int n, int w) // 记录素因子
{
	for (int i = 2; i <= n; i++)
		if (n % i == 0 && vis[i])
			v[w].push_back(i);
}
void dfs(int d, int tot)
{
	if (d == n)
	{
		ans = min(ans, tot);
		return;
	}
	else
	{
		for (auto i = 0; i < v[d].size(); i++)
		{
			if (!use[v[d][i]])
			{
				use[v[d][i]] = 1;
				dfs(d + 1, tot + v[d][i]);
				use[v[d][i]] = 0;
			}
		}
	}
}
int main(){
	for (int i = 2; i <= 1000; i++)
		if (is_prime(i))
			vis[i] = 1;
	cin >> n;
	for (int i = 0; i < n; i++)
		scanf("%d", &a[i]), get_num(a[i], i);
	dfs(0, 0);
	if (ans == 0x3f3f3f3f)
		cout << -1 << endl;
	else
		cout << ans << endl;
	return 0;
}

F-kotori和n皇后

链接:https://ac.nowcoder.com/acm/contest/940/F
来源:牛客网
 

题目描述

kotori最近在研究n皇后的问题。

所谓n皇后问题是这样的:一个n*n的地图,上面一共放n个皇后,保证任意两个皇后都不能互相攻击(每个皇后可以攻击同一行、同一列以及同一45度角斜线和135度角斜线上的所有其他皇后)。

kotori思考了很久都无法得出答案,整个人都变成琴梨了。她于是拿了一堆皇后在一个无穷大的棋盘上模拟,按照次序一共放了k个皇后。

但是,皇后的站位太复杂了,kotori甚至不知道是否存在两个皇后会互相攻击。于是她想问问聪明的你,在第i个皇后放置在棋盘上之后,是否存在两个皇后可以互相攻击?

思路: 用map记录, 每行每列, 两条对角线每种情况最先出现的点, 并记录摆到第几个时, 会出现攻击现象。

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
mapmp1;
mapmp2;
mapmp3;
mapmp4;
struct node
{
	int x, y;
}a[N];
int main(){
	int K, M = 0x3f3f3f3f;
	cin >> K;
	for (int i = 1; i <= K; i++)
	{
		scanf("%d%d", &a[i].x, &a[i].y);
		if (!mp1[a[i].x])
			mp1[a[i].x] = i;
		if (!mp2[a[i].y])
			mp2[a[i].y] = i;
		if (!mp3[a[i].x - a[i].y])
			mp3[a[i].x - a[i].y] = i;
		if (!mp4[a[i].x + a[i].y])
			mp4[a[i].x + a[i].y] = i;
		if (M != 0x3f3f3f3f)
			continue;
		if ((mp1[a[i].x] < i) || (mp2[a[i].y] < i) || (mp3[a[i].x - a[i].y] < i) || (mp4[a[i].x + a[i].y] < i)) // 出现冲突
			M = i;
	}
	int t;
	cin >> t;
	while (t--)
	{
		int num;
		scanf("%d", &num);
		if (num < M) // 在冲突前
			puts("No");
		else
			puts("Yes");
	}
	return 0;
}

G-kotori和抽卡(二)

链接:https://ac.nowcoder.com/acm/contest/940/G
来源:牛客网
 

题目描述

kotori最近喜欢上了lovelive这个游戏,因为她发现自己居然也是里面的一个人物。

lovelive有个抽卡系统。共有R、SR、SSR、UR四个稀有度,每次单抽对应稀有度的概率分别是80%,15%,4%,1%。

然而,kotori抽了很多次卡还没出一张UR,反而出了一大堆R,气得她想删游戏了。她想知道n次单抽正好出m张R卡的概率是多少?

思路: 二项分布, 计算公式P = C(n,k) *(p^k)*(1-p)^(n-k)

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
double C(double n, double m) // 用double防止溢出
{
	double cnt1 = 1, cnt2 = 1, cnt3 = 1;
	for (double i = 1; i <= n; i++)
		cnt1 *= i;
	for (double i = 1; i <= m; i++)
		cnt2 *= i;
	for (double i = 1; i <= n - m; i++)
		cnt3 *= i;
	return cnt1 / (cnt2 * cnt3);
}
int main(){
	int n, m;
	cin >> n >> m;
	double ans = 1;
	for (int i = 1; i <= m; i++)
		ans *= 0.8;
	for (int i = 1; i <= n - m; i++)
		ans *= 0.2;
	double u = C(n, m);
	printf("%.4f", ans * u);
	return 0;
}

H-andy和购物

链接:https://ac.nowcoder.com/acm/contest/940/H
来源:牛客网
 

题目描述

andy要去市场买n件货物,每件货物的价格为ai。商家为了吸引顾客,给每个买N件货物的顾客一个折扣清单,清单上有N个小于1的小数bj表示折扣。对于每个折扣bj,由用户自行决定用它使哪个货物的价格变成bj * ai,并且只能用一次。

andy想让你帮他算一下他最少的花费。

思路: 贪心排序, 最大的价格乘以最低的折扣

#include 
#include 
#include 
#include
using namespace std;
typedef long long ll;
int n, q;
const int N = 1e5 + 10;
int a[N];
double b[N];
bool cmp(int a, int b)
{
	return a > b;
}
int main(){
	int t;
	scanf("%d", &t);
	while (t--)
	{
		cin >> n;
		memset(a, 0, sizeof a);
		memset(b, 0, sizeof b);
		for (int i = 0; i < n; i++)
			cin >> a[i];
		for (int i = 0; i < n; i++)
			cin >> b[i];
		double ans = 0;
		sort(b, b + n);  // 排序
		sort(a, a + n, cmp);
		for (int i = 0; i < n; i++)
			ans += a[i] * b[i];
		printf("%.3f\n", ans);
	}
	return 0;
}

I-andy种树

链接:https://ac.nowcoder.com/acm/contest/940/I
来源:牛客网
 

题目描述

andy在他的庄园里种了n棵树,排列成一排,标号为1到n。最开始的时候n棵树的高度都是0,也就是种子刚刚被埋下,树还没有长出来。

andy会一种魔法,他每使用一次魔法,就可以让树标号落在连续区间[l, r]里的树的高度增加1。他可以使用q次这种魔法,然后他很好奇,在使用了q次魔法之后,他的所有树的高度分别是多少呢?

思路: 裸的差分数组。

#include 
#include 
#include 
using namespace std;
typedef long long ll;
int n, q;
const int N = 1e5 + 10;
int a[N];
int b[N];
int main(){
	cin >> n >> q;
	for (int i = 1; i <= q; i++)
	{
		int x, y;
		cin >> x >> y;
		a[x]++, a[y + 1]--;
	}
	for (int i = 1; i <= n; i++) // 计算原数组
	{
		b[i] = b[i - 1] + a[i];
	}
	for (int i = 1; i <= n; i++) 
		printf("%d ", b[i]);
	return 0;
}

J-andy的树被砍了

链接:https://ac.nowcoder.com/acm/contest/940/J
来源:牛客网
 

题目描述

andy又开始种树了,他觉得老用魔法不太好,这次他决定老老实实地每天种一棵树,第i天种一颗高度为hi的树,按理说老老实实种树就完事了,哪有那么多问题呢?但是他们学校有个叫kotori的人,非常爱砍树,每天都会把所有andy已经种下的树砍掉ci,如果第i天的时候某棵树的高度已经小于等于ci了,那么这棵树就会死亡,以后再也不会被砍了。并且如果到了第n天,有一些树还没被砍,那么kotori就会在第n + 1天把这些树全部砍死。

思路: 计算前缀和, 二分查找第几天该树会死亡。

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
ll a[N];
ll b[N];
int re[N];
int main(){
	ll sum = 0;
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		scanf("%lld", &a[i]);
	for (int i = 1; i <= n; i++)
		scanf("%lld", &b[i]), sum += b[i], b[i] = sum;
	for (int i = 1; i <= n; i++)
	{
		ll need = a[i] + b[i - 1];   // 加上一个b[i - 1], 因为第i天以前对当前不造成影响
		re[i] = lower_bound(b + 1, b + n + 1, need) - b;
	}
	for (int i = 1; i <= n; i++)
		cout << re[i] << " ";
	return 0;
}

 

你可能感兴趣的:(牛客)