Codeforces Round #579 (Div. 3) 题解

好多贪心啊。

A. Circle of Students

题目大意:

一群学生围成一个圈跳舞,每个学生都有一个编号,如果n个学生围成一个圈后,无论顺时针或者逆时针,总能依次从 1号到n号。

则输出"YES",否则输出"NO"

第一种做法:

直接模拟一下就行了,找出1号的位置,然后看2号在其顺时针方向还是逆时针方向,依次遍历,如果不符合条件,输出,跳出循环。

#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int main(){
	int T;
	cin>>T;
	int f = 0;
	while(T--){
		f++; 
		int A[205];
		memset(A,0,sizeof(A));
		int t;
		cin>>t;
		for(int i = 0; i < t; i++){
			cin>>A[i];
		}
		if(t==1&&A[0]==1) cout<<"YES\n";
		else{
		int cnt = 0, count = 1;
		int i = 0;
		for(i = 0; i < t;i++){   // 找到 1号的位置, 并判断应该顺时针遍历还是逆时针遍历
			if(A[i]==1){
				if(i==t-1) {
					if(A[i-1]-A[i]==1)
						cnt = -1;
					else cnt = 1;	
					break;
				}
				else{ 
					if(A[i+1] - A[i] ==1){
					cnt = 1;
					}
					else 
					cnt = -1;
					break;
				} 
			}
		}
			if(cnt==1) {
				int j = 0;
				while(count!=t){
					if(i<t-1) j = i + 1;
					else j = 0;
//					cout<<A[j]<<"    " <<A[i]<<endl;
					if(A[j] - A[i] != 1) break;
					else count++;
					if(i==t-1&&j==0) {i = j; j++;}
					else {i++;}
//					cout<<count<<" ----"<<endl;		
				}
			}
			else if(cnt==-1){
				int j = 0;
				while(count!=t){
					if(i>0)
						j = i - 1;
					else j = t - 1;
					if(A[j] - A[i] != 1) break;
					else count++;
					if(i==0&&j==t-1) {i = j; j--;}
					else i--;j--;
				}
			}
		if(count==t) cout<<"YES\n";
		else cout<<"NO\n";
//		cout<<f<<"/"<<endl;
		}
	}
	return 0;
}

第二种做法:

不需要考虑方向,如果能按1~n的顺序围成一个圈,那么,两个相邻的人的编号之差的绝对值要么是1 要么是n-1 。  

所以判断n个编号是否全都满足以上条件。 

如  3 2 1 4      3-2=1  2-1=1   4-1=3          

#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int main(){
	int T;
	cin>>T;
	while(T--){
		int f = 0,t; 
		int A[205];
		memset(A,0,sizeof(A));
		cin>>t;
		for(int i = 0; i < t; i++)	cin>>A[i];
			
		for(int i = 0; i < t - 1; i++){
			if(abs(A[i]-A[i+1])!=1&&abs(A[i]-A[i+1])!=t-1) {
				f = 1;
				break;
			} 
		}
		f ? cout<<"NO\n" : cout<<"YES\n";
	}
		return 0;
} 

B.Equal Rectangles

题目大意:

给出4n条边,让你组成n个面积相等的矩形,如果能组成,则输出"YES",否则输出"NO"

首先判断是否能组成n个矩形

 条件1:构成一个矩形的条件是,有两对相等的边,所以构成n个矩形的条件是 有2*n对相等的边。

再判断n个矩形的面积是否能相等。

 一共有2*n个对边,如果想组成 n 个面积相等的矩形,则最长对边只能与最短对边组成一个矩形,然后除去这两组对边后的最长边和最短边再次组成一个矩形,如果有面积不同的,则不能够成n个面积相等的矩形。

证明: 反正法:设最长边为 la   最短边为  lb   其他任意边为 lc     则  la > lc > lb

设最长边和除了最短边外任意一组对边组合为矩形A,面积为Sa, Sa = la*lc

最短边和除了最长边外任意一组对边组合为矩形B,面积为Sb,Sb = lc*lb

则  la*lc > lc*lb, 即此种情况两矩形面积永远不可能相等。  所以最长边只能与最短边组合。

#include <stdio.h>
#include <iostream>
#include <queue>
#include <string>
#include <algorithm> 
#include <cstring>
using namespace std;
int main(){
	int t;
	cin>>t;
	while(t--){	
		int T, flag = 1, A[10002]={0}, B[420]={0}; //A数组用来记录每条边出现的次数
		cin>>T;                                    //B数组用来保存每组对边。
		int j = 0;
		for(int i = 0; i < 4*T; i++) {
			int x;
			cin>>x;
			A[x]++;		
			if(A[x]%2==0){  
				B[j++] = x;
			} 
		}
		sort(B,B+j);  
		int cnt = B[0]*B[j-1];
		if(j!=2*T) {cout<<"NO\n";continue;} 
		for(int i =1,k = j-2; i<j/2;i++,k--){  //让最大边和最小边进行组合才有可能满足条件 
			if(B[i]*B[k]!=cnt) {flag = 0; break;} 
		}
		flag ? cout<<"YES\n" : cout<<"NO\n";
	}
	return 0;
} 

C.Common Divisors

题目大意:

给出n个整数,找出所有能被n个数整除的数。

即找出n个数的最大公约数,然后求其因子个数。

__gcd()为库函数

#include <stdio.h>
#include <iostream>
#include <math.h>
#include <algorithm>
#define sscc ios::sync_with_stdio(false)
using namespace std;
int main(){
	sscc;
	long long a;
	cin>>a;
	long long x,y;
	cin>>x;
	long long cnt = 0;
	for(long long i = 1; i < a; i++){cin>>y;	x = __gcd(x,y);	} 
	for(long long i = 1; i <= sqrt(x); i++) if(x%i==0)	cnt++,cnt++;	
	if((long long )sqrt(x) == sqrt(x)) cout<<cnt-1;
	else cout<<cnt;
	return 0;
}

D. Remove the Substring 

题目大意 :

给出两个字符串, s和t,   保证 t 一定是 s 的子串。 然后在s中删除一些子序列后, 要使 t 仍是s 的子串, 求删除子序列的最长长度为多少。

也就是求通过删除s中的一些子序列将 s 变为 t 后,所删除的最长子序列。

那么分析如何删除子序列,

一共只有两种删法。设 t = ab

第一: 删去 在 s 串中 t 串左右两端的子序列, 求MAX.  如:      baaba       可删去的最大长度为 2         

第二: 删去 在 s 串中 组成 t 串的字符之间的子序列, 求MAX, 如:  aaabb   可删去的最大长度为3

最后在比较两种删法的MAX。

那么如何求呢?

对于第一种,  求右端最长可删去子序列,则s中的t串要尽可能靠左, 如  babba    s[1] 和 s[2] 可以组成 t, s[1]和s[3] 同样可以组成 t ,显而易见肯定选择前者。

当从左向右遍历时s串时,所有第一次和t串匹配的字符,所构成的 t 串,则是距离右端最远的。 

当从右向左遍历时s串时,所有第一次和t串匹配的字符,所构成的 t 串,则是距离左端最远的。

示例: s = aaaaccccdddddddf     t = acd         

则 aaaaccccddddddddf 这三个位置构成的 t 距离右端最远。  aaaaccccddddddddf  这三个位置构成的 t 距离左端最远

一个离左端最远,一个离右端最远,所以两者相减,就是第二种删法所求的最大值。

即  aaaaccccddddddddf    a c 字符之间可最长删去6个字符。

      aaaaccccdddddddf      c d 字符之间可最长删去9个字符。
所以MAX  = 9

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
using namespace std;
const int N = 2*1e5+5;
int arr1[N];
int arr2[N];
  // 满足条件  ->  next[i] 严格小于 next[i+1];    
char s[2*N],t[2*N];   // baaba  ab  2 4  3 4   从左开始第一个满足条件的数 
                      //abcccdefgfffggasdaswezxcggg    abcfg    1238 12        125  11 12  
int main(){
	scanf("%s",s+1);
	scanf("%s",t+1);
	int len1 = strlen(s+1);
	int len2 = strlen(t+1); 
	int Max = 0;
	for(int i = 1,j = 1; i <= len1&&j<=len2; ){
		if(s[i]==t[j]){    //记录了从左到右t中每个字母在s中出现的首位置。 
			arr1[j] = i;
			++i,++j;
		}
		else ++i;
	}
	for(int i = len1,j = len2; i >= 1&&j >= 1; ){
		if(s[i]==t[j]){    //记录了从右到左t中每个字母在s中出现的首位置。 
			arr2[j] = i;
			--i,--j;
		}
		else --i;
	}
	/第二种删法。
	for(int i = 1; i < len2; i++){
		Max = max(Max, arr2[i+1] - arr1[i] -1); // 右边的减左边的  baaba
	}
	Max = max(Max,arr2[1]-1);  // 第一种删法的左右两端情况。
	Max = max(Max,len1-arr1[len2]);
	printf("%d",Max);
	return 0;	
}

E. Boxers

题目大意:

给出n个数,代表 n 个拳击手的初始体重。

要求在比赛的时候组成一队所有拳击手体重都不同的队伍, 赛前拳击手的体重可以增加一或减一,但最低为1。

求这一队最多可以有几个拳击手。

也就是说给出n个数, 这n个数可以改变加一或减一,但不能变为0,求最多可以改变为多少个不同的数。

为了尽可能的避免将某个数变化后和其他数重复,则将最大数加+1,现在得到的数一定不会和其他数重复,那么就空出了一个数,所以第二大的数加一去填补,就又空出了一个数,依次类推,如果有重复的,就不加,还有重复的就减一。

#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 150005;
int arr[N] = {0};
int vis[N] = {0};
inline bool cmp(int a, int b){
	return a>b;
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i = 0; i < n; i++){
		scanf("%d",&arr[i]);
	}
	int sum = 0;
	sort(arr, arr+n, cmp);     
	for(int i = 0; i < n; i++){
		if(!vis[arr[i]+1]) vis[arr[i]+1] = 1,++sum;
		else if(!vis[arr[i]]) vis[arr[i]] = 1, ++sum;
		else if(arr[i]-1>0&&!vis[arr[i]-1]) vis[arr[i]-1] = 1, ++sum;  
	}
	cout<<sum;
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值