AtCoder Beginner Contest 270题解 CDEF

文章提供了几个算法题目的解决方案,包括使用深度优先搜索(DFS)在树结构中找到两个节点间的简单路径,以及一个博弈问题,利用动态规划(DP)求解甲乙两人轮流取石头的最大收益。另外,还讨论了如何通过二分查找计算经过环形排列的苹果堆若干次后的剩余数量,以及构建最小生成树解决机场和码头建设的最优化问题。

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

C
题目大意:给定一个树,给出两个节点u,v,输出u,v之间的简单路径.

直接dfs即可,中间记录每个节点的父节点,然后根据其父节点一步步找。
具体细节见代码。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=4e5+10;
const double eps=1e-4;

int h[N],e[N],ne[N],idx;
int p[N]; 
int x,y,n;
void add(int a,int b){
	e[idx]=b; ne[idx]=h[a]; h[a]=idx++;
}

void dfs(int u,int fa)
{
	if(u==x){//找到直接输出 
		int t=u;
		while(p[t])
		{
			cout<<t<<" ";
			t=p[t];
		}
		cout<<y<<endl;
		return ;
	}
	for(int i=h[u];~i;i=ne[i]){
		int j=e[i];
		if(j==fa) continue;
		p[j]=u;
		dfs(j,u);
	}
}
void work()
{
	memset(h,-1,sizeof h);
	cin>>n>>x>>y;
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		add(u,v); add(v,u);
		
	}
	dfs(y,-1); //倒着做dfs,可以实现正序输出
} 

signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	
	return 0;
}

D
题目大意:共有n个石头,给定一个序列A, 甲乙两人轮流拿石头,甲先手,规则是若当前剩有k个石头,只能拿取a[j]个石头,a[j]<=k,问甲最多能拿到多少石头。

思路:刚开始根本没思路,后面看了题解好像是博弈DP,把状态表示好就很容易做好此题。 具体做法见代码。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=2e5+10;
const double eps=1e-4;

int n,k;
int a[N];
int dp[N];
void work()
{
	cin>>n>>k;
	for(int i=1;i<=k;i++){
		cin>>a[i];
	}
	//dp[i]表示当前共取i个石头,先手获得的最大值
	//(先手(T)最后一次取的石头为a[j])
	//则先手取前,后手(A)是先手,此时共有i-a[j]个石头
	//因此先手(T)可以取的石头为a[j]+i-a[j]+dp[i-a[j]]
	//状态转移为dp[i]=max(dp[i],a[j]+i-a[j]+dp[i-a[j]])
	for(int i=0;i<=n;i++){
		for(int j=1;j<=k;j++){
			if(i>=a[j]) dp[i]=max(dp[i],a[j]+i-a[j]-dp[i-a[j]]);
		}
	}
	cout<<dp[n]<<endl;
} 

signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	return 0;
}

E
题目大意:
有n堆苹果围成一个环,每堆苹果有 a i a_i ai个,若当前在 i 处,如果当前有苹果则取走一个苹果,如果没有则不取,然后都移向 i+1 处,问当取走K个苹果后,每堆苹果还剩多少个。

思路: 直接二分要循环的次数,也就是重复经过每堆苹果多少次,然后计算答案即可。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=4e5+10;
const double eps=1e-4;

int n,k;
int a[N];
int get(int x)
{
	int ans=0;
	for(int i=1;i<=n;i++){
		if(a[i]>=x) ans+=x;
		else ans+=a[i];
		
	}
	return ans;
}
void work()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	
	}
	int l=1,r=k;
	while(l<r)
	{
		int mid=l+r>>1;
		if(get(mid)<k) l=mid+1;
		else r=mid;
	}
	int x=get(r)-k;
	//cout<<x<<endl;
	for(int i=1;i<=n;i++){
		a[i]-=r;
	}
	for(int i=n;x>0;i--){
		if(a[i]>=-1) {
			a[i]++;
			x--;
		}
	}
	for(int i=1;i<=n;i++){
		if(a[i]>0) cout<<a[i]<<" ";
		else cout<<0<<" ";
	}
	cout<<endl;
} 

signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	
	return 0;
}

F
题目大意:有n个点,在第 i 个点建机场要花 x i x_i xi元,建码头要花 y i y_i yi元。在 A i A_i Ai B i B_i Bi两点修建道路需要 z i z_i zi元。
若两点都有机场或码头或一个有机场一个有码头或有道路连接,则这两点可以互通。

问最少需要多少钱可以实现n点互通。

思路:做法很明显是最小生成树,但是关键怎么建图(连边),一个巧妙地方法是,建立两个虚拟源点,n+1,n+2, 然后每个点的机场造价相当于往n+1这个点连边的边权,同理,每个点的码头造价相当于往n+2这个点连边的边权。 然后跑四遍最小生成树算法即可(无机场码头,无机场有码头,无码头有机场,都有)。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=4e5+10;
const double eps=1e-4;
int n,m;
int a[N],b[N],p[N];
int x[N],y[N],z[N];
struct node{
	int x,y,z;
	bool operator <(const node &t) const
	{
		return z<t.z;
	}
}e[N];
int cnt;
int find(int x)
{
	if(x!=p[x]) p[x]=find(p[x]);
	return p[x];
}
void init()
{
	cnt=0;
	for(int i=1;i<=n+2;i++) p[i]=i;
}
void merge(int x,int y)
{
	int px=find(x),py=find(y);
	p[px]=py;
}
int kru(int nx)
{
	sort(e+1,e+1+cnt);
	int ans=0,num=0;
	
	for(int i=1;i<=cnt;i++){
		int x=e[i].x,y=e[i].y;
		if(find(x)!=find(y)) {
			merge(x,y);
			ans+=e[i].z;
			num++;
			if(num==nx-1) return ans;
		}
	}
	return 1e18;
}
void work()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++) cin>>b[i];
	for(int i=1;i<=m;i++){
		cin>>x[i]>>y[i]>>z[i];
	}
	
	int ans=1e18;
	
	for(int k=0;k<=3;k++){
		init();
		for(int i=1;i<=m;i++) e[++cnt]={x[i],y[i],z[i]};
		if(!k) ans=min(ans,kru(n));
		if(k==1){
			for(int i=1;i<=n;i++){
				e[++cnt]={i,n+1,a[i]};
			}
			ans=min(ans,kru(n+1));
		}
		if(k==2){
			for(int i=1;i<=n;i++){
				e[++cnt]={i,n+2,b[i]};
			}
			ans=min(ans,kru(n+1));
		}
		if(k==3){
			for(int i=1;i<=n;i++){
				e[++cnt]={i,n+1,a[i]};
			}
			for(int i=1;i<=n;i++){
				e[++cnt]={i,n+2,b[i]};
			}
			ans=min(ans,kru(n+2));
		}
	}
	cout<<ans<<endl;
} 

signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值