Codeforces Round #408 (Div. 2)

本文解析了四道经典算法题目,包括寻找最短路径、跟踪物品移动、优化攻击策略及最大边删除等,通过示例代码详细展示了每道题目的解决思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A

#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;
const int qq = 1e5 + 10;
int num[qq];

int main(){
	int n, m, k;
	scanf("%d%d%d", &n, &m, &k);
	for(int i = 1; i <= n; ++i){
		scanf("%d", num + i);
	}
	int l = m;
	int minx = 1e9;
	while(l >= 1){
		if(num[l] == 0){
			l--;
			continue;
		}
		if(k >= num[l]){
			minx = m - l;
			break;
		}
		l--;
	}
	int r = m;
	while(r <= n){
		if(num[r] == 0){
			r++;
			continue;
		}
		if(k >= num[r]){
			minx = min(minx, r - m);
			break;
		}
		r++;
	}
	printf("%d\n", 10 * minx);
	return 0;
}


B

题意:有n个杯子放在一行中, 然后有一些杯子下面有洞,给出m个洞的位置,然后给出k次交换的杯子编号,问最终骨头在那个位置,如果骨头在交换中落入洞中那么很显然骨头的位置就不会发生改变了。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int qq = 1e6 + 19;
bool vis[qq];
int main(){
	memset(vis, false, sizeof(vis));
	int n, m, k;
	scanf("%d%d%d", &n, &m, &k);
	int id;
	for(int i = 0; i < m; ++i){
		scanf("%d", &id);
		vis[id] = true;
	}
	int posi = 1;
	bool flag = true;
	if(vis[1] == true){
		flag = false;
	}
	int a, b;
	for(int i = 0; i < k; ++i){
		scanf("%d%d", &a, &b);
		if(!flag)	continue;
		if(a != posi && b != posi)	continue;
		if(a == posi){
			posi = b;
			if(vis[b]){
				flag = false;
			}
		}else if(b == posi){
			posi = a;
			if(vis[a]){
				flag = false;
			}
		}

	}
	printf("%d\n", posi);
	return 0;
}


C

题意:给出n个结点,每个结点有一个值, 然后有n-1条边连接这n各点,保证是一颗树, 现在规则是这样,第一次可以攻击任意一个结点,之后攻击的结点要满足以下条件

1.被攻击点是一个未被攻击的点

2.该被攻击点的相邻点一定要存在一个已经被攻击过的点

3.该被攻击点的值不得超过某个值x

另外你每选择攻击一个点,那么这个点的相邻点和半相邻点的值都会加1,如果你的相邻点是个已经被攻击的点那么由该被攻击点衍生过去的半相邻点的值就不用加1,这种情况视为阻断

问这个x值最少可以是多少

思路:我们可以这样想象除掉第一个攻击点,其他各点值只可能会 +1 或者+2,那么我们可以想到枚举第一个被攻击点,然后对其相邻点+1, 然后对剩下点的最大值+2,这样就能算出第一次攻击某个点的最小x

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>

using namespace std;
#define LL long long
#define pb push_back
#define mst(a, b)	memset(a, b, sizeof(a))
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 3e5 + 10;
vector<int> G[qq];
multiset<int, greater<int> > myset;
int num[qq];
void Del(int x){
	__typeof(myset.begin()) t;
	t = myset.find(x);
	myset.erase(t);
}

int main(){
	int ans = 2e9 + 10;
	int n;
	scanf("%d", &n);
	int a, b;
	REP(i, 1, n){
		scanf("%d", num + i);
		myset.insert(num[i]);
	}
	REP(i, 1, n - 1){
		scanf("%d%d", &a, &b);
		G[a].pb(b), G[b].pb(a);
	}
	REP(i, 1, n){
		Del(num[i]);
		int ma = num[i];
		for (int y : G[i]){
			Del(num[y]);
			ma = max(ma, num[y] + 1);
		}
		if(!myset.empty()){
			ma = max(ma, *myset.begin() + 2);
		}
		for(int y : G[i]){
			myset.insert(num[y]);
		}
		myset.insert(num[i]);
		ans = min(ans, ma);
	}
	printf("%d\n", ans);
}


__typeof(x) 可以改用 auto 貌似还快一点

突然发现另外一种方法特别好,我们上面推出了每一个点的值最多会增加2, 那么其实我们只需要找出第一大和第二大的数,其他数可以不用考虑了

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>

using namespace std;
#define LL long long
#define pb push_back
#define mst(a, b)	memset(a, b, sizeof(a))
#define REP(i, x, n)	for(int i = x; i < n; ++i)
const int qq = 3e5 + 10;
int num[qq], mx1[qq], mx2[qq];

int main(){
	int n;
	int m1, m2;
	m1 = m2 = -1e9 - 10;
	scanf("%d", &n);
	REP(i, 0, n){
		scanf("%d", num + i);
		if(num[i] > m1){
			m2 = m1;
			m1 = num[i];
		}else if(num[i] > m2){
			m2 = num[i];
		}
	}
	int x, y;
	x = y = 0;
	REP(i, 0, n){
		if(num[i] == m1)	x++;
		if(num[i] == m2)	y++;
	}
	int a, b;
	REP(i, 1, n){
		scanf("%d%d", &a, &b);
		if(num[a - 1] == m1)	mx1[b - 1]++;
		if(num[a - 1] == m2)	mx2[b - 1]++;
		if(num[b - 1] == m1)	mx1[a - 1]++;
		if(num[b - 1] == m2)	mx2[a - 1]++;
	}
	int ans = 2e9 + 10;
	REP(i, 0, n){
		int tmp = num[i];
		if(mx1[i] > 0)	tmp = max(tmp, m1 + 1);
		if(mx2[i + 1] > 0)	tmp = max(tmp, m2 + 1);
		if(num[i] == m1)	mx1[i]++;
		if(num[i] == m2)	mx2[i]++;
		if(mx1[i] < x)	tmp = max(tmp, m1 + 2);
		if(mx2[i] < y)	tmp = max(tmp, m2 + 2);
		ans = min(ans, tmp);
	}
	printf("%d\n", ans);
	return 0;
}




D

题意:n个点,n-1条边,其中m个点有警察局,每个警察局的覆盖范围是距离这个警察局在k步内,现在保证图中所有结点至少被一个警察局覆盖, 问最多可以删除多少条边,还是保证图中任意一个结点都至少能被一个警察局覆盖。

思路:首先分析可以知道,在这个图中每删除一条边就会增加一个联通分量,而我们又知道每一个联通分量都至少有一个警察局,所以我们又知道最多可以删除m - 1条边,对于任意一条边 a-b, 如果着条边可以删除,那么它满足,a可以通过其他边到达一个最近的警察局 不等于 b可以通过其他边到达一个最近的警察局,就是两个结点的最近警察局不相同。所以我们可以把所有警察局扔进队列进行bfs

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mk make_pair
#define mst(Arr, x)	memset(Arr, x, sizeof(Arr))
#define REP(i, x, n)	for(int i = x; i < n; ++i)
const int qq = 3e5 + 10;
vector<int> G[qq], ls;
map<pill, int> mp, uni;
int dis[qq];
int n, k, d;
int ans;
queue<int> Q;
void Bfs(){
	while(!Q.empty()){
		int u = Q.front();
		Q.pop();
		REP(i, 0, (int)G[u].size()){
			int v = G[u][i];
			if(dis[v] == -1){
				dis[v] = dis[u];
				Q.push(v);
			}else if(dis[v] != dis[u]){
				if(uni[mk(u, v)] || uni[mk(v, u)])	continue;
				uni[mk(u, v)] = uni[mk(v, u)] = true;
				ans++;
				ls.pb(mp[mk(u, v)]);
			}
		}
	}
}
int main(){
	scanf("%d%d%d", &n, &k, &d);
	int x;
	mst(dis, -1);
	REP(i, 0, k){
		scanf("%d", &x);
		dis[x] = x;
		Q.push(x);
	}
	REP(i, 1, n){
		int a, b;	scanf("%d%d", &a, &b);
		mp[mk(a, b)] = i;
		mp[mk(b, a)] = i;
		G[a].pb(b), G[b].pb(a);
	}
	ans = 0;
	Bfs();
	sort(ls.begin(), ls.end());
	printf("%d\n", ans);
	REP(i, 0, ans){
		printf("%d ", ls[i]);
	}
	puts("");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值