codeforces Round 756 div3记录

A

题意:给出一个数字,每次可以进行的操作是选择前n个数进行翻转(如2365->5632),问最少几次可以把这个数字转化为偶数

思路:

1.本身为偶数,ans=0

2.不满足1,最高位为偶数:ans=1,直接选择整个数字进行反转

3.不满足2,但是中间含有偶数:ans=2,先把偶数反转到最高位,然后选择整个数字反转

4.不满足3,即各个数位均为奇数,无解,ans=-1

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<iomanip>
using namespace std;
typedef long long ll;
const long long ll_inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int Max=1000+5;
const int Mod=1e9;
//cout<<setw(8)<<fixed<<setprecision(3);
//ios::sync_with_stdio(false);
//priority_queue<int,vector<int>,greater<int> >
#define _for(i,n) for(int i=0;i<n;++i)
#define _rep(i,n) for(int i=1;i<=n;++i)
int main(){
	int n,a;
	cin>>n;
	while(n--){
		cin>>a;
		if(a%2==0){
			cout<<'0'<<'\n';
			continue;
		}
		int flag=0,flag1;
		while(a>0){
			if((a%10)%2==0){
				flag=1;
			}
			flag1=a%2;
			a/=10;
			
		}
		if(flag&&!flag1)
		cout<<"1\n";
		else if(flag&&flag1)
		cout<<"2\n";
		else
		cout<<"-1\n";
	} 
}

B

题意:给出n个工程师,m个数学家,要求组成四人组,且不能由4个工程师或4个数学家组成(即至少有一个工程师和数学家),问最多能组成多少四人组

我的思路:设n比m大,若n ≥ m+2,则以3+1的形式先组,直到n<m+2,然后再以2+2的形式组成min(n/2,m/2)队,

看了别人的思路:答案等于min ⁡( ⌊ (a + b)/ 4 ⌋ , a , b ) 。。。思路大概是把纯1+3和2+2,1+3混合的比较,反正我是想不到的

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<iomanip>
using namespace std;
typedef long long ll;
const long long ll_inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int Mod=1e9+7;
const int Max=1000;
int main(){
	int n;
	cin>>n;
	int a,b,c;
	for(int i=0;i<n;i++){
		int ans=0;
		cin>>a>>b;
		if(a==b){
			ans=a/2;
		}else{
			if(a<b){
				c=a;
				a=b;
				b=c;
			}
			if(a-b>=2){
					int aa=a/3,bb=b;
					int mi=min(aa,bb);
					mi=min(mi,(a-b)/2);
					ans+=mi;
					a-=mi*3;
					b-=mi;
				}
			ans+=b/2;
		}
		cout<<ans<<'\n';
	}
}
 

C

给出一个数组,然后进行以下操作:

题意:选出数组两头较小的数,如果在左边则加到新数组的左边,如果在右边则加到新数组的右边,如此进行可以得到一个新数组,原数组第一次选出来的数在新数组没有左右之分,直接放进去即可,原数组最后选出来的数同样没有左右之分,可以任意加在新数组的左边或者右边。现在给出新数组,求一个可行的原数组。

思路:容易知道原数组的最大数一定是最后选出来的,即一定会出现在新数组的两边,如果新数组的最大数不是在两头则一定不可行。如果在的话,直接反转其余数组即可得到一个可行的数组。因为最大的数在原数组两头时,原数组就只能按照固定一头的顺序输出了

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<iomanip>
using namespace std;
typedef long long ll;
const long long ll_inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int Max=100000*2+5;
const int Mod=1e9;
//cout<<setw(8)<<fixed<<setprecision(3);
//ios::sync_with_stdio(false);
//priority_queue<int,vector<int>,greater<int> >
#define _for(i,n) for(int i=0;i<n;++i)
#define _rep(i,n) for(int i=1;i<=n;++i)
int main(){
	int n;
	cin>>n;
	while(n--){
		int t;
		cin>>t;
		int a[Max],ma=0;
		for(int i=0;i<t;i++){
			cin>>a[i];
			ma=max(ma,a[i]);
		}
		if(ma!=a[0]&&ma!=a[t-1]){
			cout<<"-1\n";
			continue;
		}else{
			for(int i=t-1;i>=0;i--){
				cout<<a[i]<<' ';
			}
			cout<<'\n';
		}
	}
}

此次div3止步于此,总结一下就是用代码实现思想的能力还很欠缺,代码也有很多地方很冗余,前面稍简单的题还需要很多时间去思考,而且思考也很不全面。还有很长的路要走


D

题意:有一棵树,给出每个节点的父节点(根节点是本身),再给出一个节点的排序,排序规则如下:每个节点的距离是这个节点到根节点所经过所有的边的权值之和(根节点为0)。现在要求给出每条边的可能的权值。这些权值可能相等,但最终的距离一定不相等

先给出我的超时代码

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<iomanip>
using namespace std;
typedef long long ll;
const long long ll_inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int Max=100000*2+5;
const int Mod=1e9;
//cout<<setw(8)<<fixed<<setprecision(3);
//ios::sync_with_stdio(false);
//priority_queue<int,vector<int>,greater<int> >
#define _for(i,n) for(int i=0;i<n;++i)
#define _rep(i,n) for(int i=1;i<=n;++i)
struct node{
	int fa;
	int dis;
}tr[Max];
int main(){
	int t;
	scanf("%d",&t);
	//cin>>t;
	while(t--){
		int n,a,root;
		scanf("%d",&n);
		//cin>>n;
		_rep(i,n){
			scanf("%d",&tr[i].fa);
			//cin>>tr[i].fa;
			tr[i].dis=-1;
			if(i==tr[i].fa){
				root=i;
				tr[i].dis=0;
			}
		}
		int flag=1,Min=1;
		_for(i,n){
			scanf("%d",&a);
		//	cin>>a;
			if(i==0&&a!=root)
			flag=0;
			if(a==root)
			continue;
			int sum=0;
			while(a!=root){
				if(tr[a].dis==-1){
					tr[a].dis=Min+1;
				}
				sum+=tr[a].dis;
				a=tr[a].fa;	
			}
			if(sum<Min)
			flag=0;
			else
			Min=sum;
		}
		if(flag){
			_rep(i,n){
				printf("%d ",tr[i].dis);
				//cout<<tr[i].dis<<' '; 
			}
			printf("\n");
		//	cout<<'\n';			
		}else
		printf("-1\n");
	//	cout<<"-1\n";
	}
}

超时思路:根据给出的排序,指定一个最小值Min,和一个和sum,从当前节点到父节点,如果还没赋值则赋值为min,否则沿用已经赋过的值(可能在循环之前已经赋过值)同时相加储存于sum中,然后判断如果sum小于Min,则无法满足排序的要求(例如d1+d2<d1这样的情况),否则更新Min。

这样虽然能保证答案的正确性,但是对每个节点都进行了一次循环找根,当遇到那种退化为链状的树的数据,复杂度O(n!)一定会超。。

官方题解:首先考虑哪些排序是非法的:

1.第一个不是树的根,这很好理解,根的距离是0,其他节点的距离一定是正数

2子节点的距离不会比父节点小。因为每个点到根的距离是确定的

下面开始填充数组 ,设dis[i] 是点i到根的距离

1.dis[ p[1] ]=0,根的距离是0

2. 对于 2 ≤ p[i] ≤ n,当前最大的距离就是 p[i-1],那么只需要为dis[ p[i] ] 赋上dis[ p[i-1] ]+1即可,记得检查dis[ b[ p[i] ] ]是否赋值,因为这是这个节点的父节点的距离,如果子节点先被赋值,那么距离就会比父节点大了。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<iomanip>
using namespace std;
typedef long long ll;
const long long ll_inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int Max=100000*2+5;
const int Mod=1e9;
//cout<<setw(8)<<fixed<<setprecision(3);
//ios::sync_with_stdio(false);
//priority_queue<int,vector<int>,greater<int> >
#define rep(i,s,e) for(int i=s;i<=e;++i)
#define don(i,s,e) for(int i=s;i>=e;--i)
int b[Max],p[Max],dis[Max];
void solve(){
	memset(dis,-1,sizeof(dis));
	int n;
	cin>>n;
	rep(i,1,n) cin>>b[i];
	rep(i,1,n) cin>>p[i];
	if(b[p[1]]!=p[1]){//判断第一个是不是根 
		cout<<"-1"<<'\n';
		return;
	}
	dis[p[1]]=0;
	rep(i,2,n){
		if(dis[b[p[i]]]==-1){
			cout<<"-1"<<'\n';
			return ;
		}
		dis[p[i]]=dis[p[i-1]]+1;
	}
	rep(i,1,n){
		cout<<dis[i]-dis[b[i]]<<' ';
	}
	cout<<'\n';
}
int main(){
	int t;
	cin>>t;
	while(t--)
	solve();
}

E_easy

题意:给出一颗无向树,每个节点最多连接两个节点。主角在其中某一个节点,主角的一些朋友在其他的任意节点(不重叠),主角和他的朋友每次可以移动到这个节点的任意相邻节点,也可以不移动,问主角是否能移动到一个除了开始以外的节点,使得这个节点只与一个节点相连(叶节点)

思路:记录两个数组dp1,dp2,分别表示每个点到朋友节点最近距离和每个点到主角所在点的最小距离,然后再进行一次dfs,看能不能走到叶节点,每个点能走的条件是dp2<dp1,即主角能先走到这个点。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<iomanip>
using namespace std;
typedef long long ll;
const long long ll_inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int Max=100000*2+5;
const int Mod=1e9;
//cout<<setw(8)<<fixed<<setprecision(3);
//ios::sync_with_stdio(false);
//priority_queue<int,vector<int>,greater<int> >
#define rep(i,s,e) for(int i=s;i<=e;++i)
#define don(i,s,e) for(int i=s;i>=e;--i)
int dp1[Max],dp2[Max];//dp1:各节点到最近的friend的距离,dp2:各节点到根节点的距离 
vector<int> G[Max];
void dfs1(int x,int fa){//求到最近的friend的距离
	if(dp1[x]==0) return;
	int t=inf;
	for(int e:G[x]){
		//cout<<' '<<e<<' '<<fa<<'\n';
		if(e==fa) continue;
		dfs1(e,x);
		t=min(t,dp1[e]);
	}
	dp1[x]=t+1;
}
void dfs2(int x,int fa){
	if(x!=1) dp2[x]=dp2[fa]+1;
	for(int e:G[x]){
		if(e==fa) continue;
		dfs2(e,x);
	}
}
int dfs3(int x,int fa){
	if(dp1[x]<=dp2[x]) return 0;
	if(G[x].size()==1&&x!=1) return 1;
	int f=0;
	for(int e:G[x]){
		if(e==fa) continue;
		f|=dfs3(e,x);
	}
	return f;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,k;
		cin>>n>>k;
		rep(i,1,n){
			G[i].clear();
			dp1[i]=inf;
			dp2[i]=0;
		}
		rep(i,1,k){
			int t;
			cin>>t;
			dp1[t]=0;
		}
		int a,b;
		rep(i,1,n-1){
			cin>>a>>b;
			G[a].push_back(b);
			G[b].push_back(a);
		}
		dfs1(1,1);
		dfs2(1,1);
		if(dfs3(1,1)) cout<<"YES\n";
    	else cout<<"NO\n";
	}
}

还是别人的代码,我太弱了


E_hard

题意:与easy版本基本相同,问的是最少需要几个朋友来让主角输掉(有可能主角能赢)

思路:在dfs3的地方进行修改,加个ans++就行了

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<iomanip>
using namespace std;
typedef long long ll;
const long long ll_inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int Max=100000*2+5;
const int Mod=1e9;
//cout<<setw(8)<<fixed<<setprecision(3);
//ios::sync_with_stdio(false);
//priority_queue<int,vector<int>,greater<int> >
#define rep(i,s,e) for(int i=s;i<=e;++i)
#define don(i,s,e) for(int i=s;i>=e;--i)
int dp1[Max],dp2[Max];//dp1:各节点到最近的friend的距离,dp2:各节点到根节点的距离 
vector<int> G[Max];
void dfs1(int x,int fa){//求到最近的friend的距离
	if(dp1[x]==0) return;
	int t=inf;
	for(int e:G[x]){
		if(e==fa) continue;
		dfs1(e,x);
		t=min(t,dp1[e]);
	}
	dp1[x]=t+1;
}
void dfs2(int x,int fa){
	if(x!=1) dp2[x]=dp2[fa]+1;
	for(int e:G[x]){
		if(e==fa) continue;
		dfs2(e,x);
	}
}
int ans;
int dfs3(int x,int fa){
	if(dp1[x]<=dp2[x]){
		ans++;
		return 0;
	}
	if(G[x].size()==1&&x!=1) return 1;
	int f=0;
	for(int e:G[x]){
		if(e==fa) continue;
		f|=dfs3(e,x);
	}
	return f;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,k;
		ans=0;
		cin>>n>>k;
		rep(i,1,n){
			G[i].clear();
			dp1[i]=inf;
			dp2[i]=0;
		}
		rep(i,1,k){
			int t;
			cin>>t;
			dp1[t]=0;
		}
		int a,b;
		rep(i,1,n-1){
			cin>>a>>b;
			G[a].push_back(b);
			G[b].push_back(a);
		}
		dfs1(1,1);
		dfs2(1,1);
		if(dfs3(1,1)) cout<<"-1\n";
    	else cout<<ans<<"\n";
	}
}

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

始归零

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值