Codeforces Round #787 (Div. 3)题解

A-

题目大意:

方法: 直接模拟

代码:

#include<iostream>

using namespace std;

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int a,b,c,x,y;
		cin>>a>>b>>c>>x>>y;
		int temp=0;
		if(a-x<0)
		{
			temp+=x-a;
		}
		if(b-y<0)
		{
			temp+=y-b;
		}
		
		if(c>=temp)
		{
			cout<<"YES"<<endl;
		}else
		{
			cout<<"NO"<<endl;
		}
	}
	return 0;
}

B-

题目大意: 给一个序列,可以让其中一数除以2向下取整,使得序列是严格递增,输出最小这样的操作数

方法: 模拟,从后向前遍历(如果从前向后,发现一遍遍历是不行的,因为你不知道当前这个数该除以二除到多小,而从后向前,最后一个数肯定是不动的),如果这个数比后一个数大的话,就除以二向下取整,直到这个数为0。然后判断是否严格递增

代码:

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int N=35;
int a[N];
void solve(void)
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	int cnt=0;
	bool flag=false;
	for(int i=n-1;i>=1;i--)
	{
		while(a[i]>=a[i+1])
		{
			if(a[i]==0)
			{
				break;
			}
			a[i]/=2;
			cnt++;
		}
		if(a[i]>=a[i+1])
		{
			flag=true;
			break;
		}
	}
	
	if(flag)
	{
		cout<<-1<<endl;
	}else
	{
		cout<<cnt<<endl;
	}
	return;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

C-

题目大意:给定一个代表物体的状态的序列,输出可能是小偷的数量

方法:  规律是前无0,后无1,那么就可以被怀疑。可以双指针计算。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>


using namespace std;
const int N=200010;
char s[N];
bool c1[N],c2[N];
int main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);
	
	int t;
	cin>>t;
	while(t--)
	{
		cin>> s+1;
		int len=strlen(s+1);
		memset(c1,false,sizeof c1);
		memset(c2,false,sizeof c2);
		bool flag=true;
		for(int i=1;i<=len;i++)
		{
			c1[i]=flag;
			if(s[i]=='0')
			{
				flag=false;
			}
		}
		flag=true;
		for(int i=len;i>=1;i--)
		{
			c2[i]=flag;
			if(s[i]=='1')
			{
				flag=false;
			}
		}
		
		int cnt=0;
		for(int i=1;i<=len;i++)
		{
			if(c1[i]&&c2[i])
			{
				cnt++;
			}
		}
		
		cout<<cnt<<endl;
	}
	
	return 0;
}

D-

题目大意: 给一个树,路径只能由父节点到子节点,把这棵树分为若干条路径,输出最小的路径数,并且输出每条路径长度和路径本身

方法:  因为n最多200000,用一个top数组记录每个点的父节点,然后记录每个叶节点,从叶节点向上找路,然后存储每条路径。

代码:

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

using namespace std;

const int N=200010;
int f[N];
bool st[N];
bool flag[N];
vector<int> a[N];
vector<int> num;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		memset(st,false,sizeof st);
		memset(flag,false,sizeof flag);
		int n;
		cin>>n;
		int root;
		for(int i=1;i<=n;i++)
		{
			cin>>f[i];
			if(i==f[i])
			{
				root = i;
			}
			st[f[i]]=true;
		}
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
		
			int cnt1=0;
			if(!st[i]||n==1)
			{
				int j=i;
				while(!flag[j])
				{
					cnt1++;
					a[cnt].push_back(j); 
					flag[j]=true;
					j=f[j];
				}
				num.push_back(cnt1);
				cnt++;
			}
		}
		cout<<cnt<<endl;
		for(int i=0;i<cnt;i++)
		{
			cout<<num[i]<<endl;
			for(int j=a[i].size()-1;j>=0;j--)
			{
				cout<<a[i][j]<<' ';
			}
			a[i].clear();
			num.clear();
			cout<<endl;
		}
	}
	return 0;
}

E-

题目大意:给定一个小写字母序列,和操作数,每次操作都可以把序列中每个字母变成前一位,输出操作后字典序最小的序列

方法:字典序最小,那么肯定是从前向后枚举,每次都要尽最大操作数,把当前的字母变得小。

只有小写字母,那么开一个26大小的布尔数组记录该字母是否可以变为前一位,然后将整个序列符合条件的字母变换输出即可

代码:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=200010;
int a[N];
bool st[26];

void solve(void)
{
	int n,k;
	cin>>n>>k;
	memset(st,false,sizeof st);
	for(int i=1;i<=n;i++)
	{
		char x;
		cin>>x;
		a[i]=(int)(x-'a');
	}

	for(int i=1;i<=n;i++)
	{
		int j=a[i];
		while(k&&j&&!st[j])
		{
			st[j]=true;j--;
			k--;
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		int j=a[i];
		while(st[j])
		{
			j--;
			a[i]--;
		}
	}
	for(int i=1;i<=n;i++)
	{
		cout<<(char)(a[i]+'a');
	}
	cout<<endl;
}
int main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

F-

题目大意:给一棵树,有起点和终点,还有若干任务点。你要从起点出发,到达所有的任务点(不限顺序),然后最后到达终点,求最小步数

方法:  去任务点是需要 来 和 回 两次,所以是2倍的步数,然后我们和D题一样的思路,我们用一个数组存储一个点的父节点。把任务点和终点都当作“叶子”然后向上走。最后bfs一遍,减去起点到终点的最短路。得到的就是答案。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef pair<int,int> PII;

const int N=2e5+10;
int w[N],dis[N],top[N],vis[N];
vector<int> gra[N];

void bfs(int x)
{
	queue<PII> q;
	q.push({x,0});
	top[x]=x;
	while(q.size())
	{
		PII now=q.front();
		q.pop();
		int u=now.first;
		dis[u]=now.second;
		
		for(int i=0;i<gra[u].size();i++)
		{
			int v=gra[u][i];
			if(top[v])
			{
				continue;
			}
			top[v]=u;
			q.push({v,now.second+1});
		}
	}
}

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n,m,x,y;
		cin>>n>>m>>x>>y;
		
		for(int i=0;i<m;i++)cin>>w[i];
		w[m] = y;
		
		for(int i=1;i<n;i++)
		{
			int a,b;
			cin>>a>>b;
			gra[a].push_back(b);
			gra[b].push_back(a);
		}
		
		bfs(x);
		
		int ans=0;
		vis[x]=1;
		
		for(int i=0;i<=m;i++)
		{
			int temp=w[i];
			
			while(vis[temp]==0)
			{
				vis[temp]=1;
				ans+=2;
				temp=top[temp];
			}
		}
		ans-=dis[y];
		
		cout<<ans<<endl;
		for(int i=1;i<=n;i++)
		{
			vis[i]=top[i]=0;
			gra[i].clear();
		}
	}
	return 0;
}

G-

题目大意:给你一个序列,每次都可以把一个数的1移到相邻另一个数上,最终使这个序列不递增

方法:  动态规划。这位大佬讲的不错G题题解

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值