大一训练4题解

A题

Problem - 1728A - Codeforces

 题目大意:有n种颜色球,每种颜色球有cnt个球,且球的总数为奇数,每次取不同的两个颜色各取一个球,问最后可能剩下球的颜色的编号。

数量最大的的一堆球一定可以剩下,让其他颜色先配对,剩下的一定比最大值小。

#include<stdio.h>
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
  {
	 int pos,maxn=-1,x,n,i;
	 scanf("%d",&n);
	 for(i=1;i<=n;i++)
	 {
		scanf("%d",&x);
		if(x>maxn)
		{
			maxn=x;
			pos=i;
		}
	 }
	 printf("%d\n",pos);
  }
    return 0;
} 

B题

Problem - 1627A - Codeforces

题目大意:给定n行m列的矩阵以及r,c,矩阵的每个单元为W代表白色或B代表黑色。每次操作可以选择一个黑色的单元让其所在的行或者列全变为黑色,问最少多少次操作让给r行c列的这一单元变为黑色,如果不能变为黑色则输出-1。

首先,如果整个矩阵全为白色,那么无法操作则不可能让所给单元变为黑色。

那么如果矩阵有黑色,则考虑以下三种情况:1、给定单元格已经是黑色,则需要操作数为0。2、给定单元格不是黑色,但其所在的列或者行有黑色,那么一次操作即可。3、给定单元格所在和行与列都没有黑色,那么两次操作即可,因为可以选择任意一个黑色染一行或者一列,这样给定单元格所在的行或者列一定有黑色了,再进行一次操作即可。

#include<stdio.h>
char a[100][100];
int n,m,r,c;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int row[60]={0},col[60]={0},i,j,flag=0;
		scanf("%d %d %d %d",&n,&m,&r,&c);
		for(i=0;i<n;i++)
		scanf("%s",a[i]);
		for(i=0;i<n;i++)
		{
			for(j=0;j<m;j++)
			{
				if(a[i][j]=='B')
				{
					flag=1;
					row[i+1]++;
					col[j+1]++;
				}
			}
		}
		if(flag==0)
		{
			printf("-1\n");
			continue;
		}
		if(a[r-1][c-1]=='B')
		printf("0\n");
		else if(row[r]>0||col[c]>0)
		printf("1\n"); 
		else
		printf("2\n");
	}
} 

 C题

Problem - 1536A - Codeforces

题目大意:给定一个长度为n的序列,这个序列由n个不同的数构成,询问是否可以通过在序列里加数的方式,使得最后这个序列任意两个数相减的绝对值在这个序列中,如果可以,输出yes以及增加数字后的序列长度还有整个序列,反之输出no。

序列中有负数肯定是不行的,举个例子:-2,7这个长度为2的序列,7-(-2)=9,所以9必须加进来,但是9加进来后,11也必须加进来,这就陷入了死循环。

序列中没有负数的时候,看数据范围,一开始给的每一个数的绝对值都不大于100,而且最后的序列长度只要不大于300就行。没有负数的话,所有给的数都是0-100之间,并且原序列所有数是不同的。所以我们利用贪心的思想,只要没有负数,就可以就把序列变成0,1,2,3......99,100。

#include<stdio.h>
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,i,flag=0;
		int a[110];
		scanf("%d",&n);
		for(i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]<0)
			flag=1;
		}
		if(flag==1)
		printf("NO\n");
		else
		{
			printf("YES\n");
			printf("101\n");
			for(i=0;i<=100;i++)
			printf("%d ",i);
		}
		printf("\n");
	}
}

D题

Problem - 1713B - Codeforces

 题目大意:给定一个长度为n的序列,每次操作可以选择一个子区间,让这个子区间内的所有数-1,问这个序列的排列是否是其所有排列中需要操作次数最小的一种。

贪心思想,需要的次数最小,那么一定是最大值。将元素从小到大排序或者从大到小排序后,就可以使得操作数最小,即为最大值。

 这是从小到大排序的情况,从大到小返过来就行,所以一个序列满足从小到大排列,从大到小排列或者先从小到大再从大到小既满足操作次数最小。出现先从大到小再从小到大的情况是不满足的,从图中可以看出,从小到大是从左边往右边收缩,从大到小是从右边往左边缩,所以前者只能再左,后者只能在后。

#include<stdio.h>
int a[100010];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,flag=-1,i;
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
		for(i=1;i<n;i++)
		{
			if(flag<0&&a[i]>a[i+1])//是否达到了山峰
			flag=1;
			if(flag>0&&a[i]<a[i+1])//达到了山峰就只能往下降了
			{
			flag=2;
			break;
		    }
		}
		if(flag==2)
		printf("NO\n");
		else
		printf("YES\n");
	} 
	return 0;
}

E题

https://codeforces.com/problemset/problem/1694/B

题目大意:给定一个01字符串,可以让01合并为1,10合并为0,输出有多少子区间最后可以合并为一个字符。

首先每个字符就是一个长度为1的区间,当区间长度大于0时: 

 

 

考虑三种情况。1、前面所有的字符都与最后一个字符不同,那么最后一个字符可以吃掉前面所有不同的字符。2、前面有与最后一个字符相同的字符,也有不同的字符 ,但是末尾两个字符不同,如图中2所示。前面如果有1的话可以由相邻的0吃掉,如果没有相邻的0吃掉,那么最末尾的0可以吃掉,所以最后可以合并为一个字符。3、最后两个字符相同,最后两个字符相同那么这两个字符无法合并。

所以我们遍历末尾,如果末尾的两个字符不一样,那么以这两个字符为末尾的所有区间都满足条件。

#include<stdio.h>
char a[200010];
int n;
void solve()
{
	int i;
	long long res=0;
	scanf("%d",&n);
	scanf("%s",a);
	for(i=0;i<n;i++)
	{
		if(i==0)
		{
			res++;
			continue;
		}
	    if(a[i]!=a[i-1])
	    res+=(i+1);
	    else
	    res++;	
	}
	printf("%lld\n",res);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	solve();
	return 0;
}

F题

Problem - 1421A - Codeforces

题目大意:t次查询,每次查询给定a,b问所有整数中(x^a)+(x^b)的最小值。

先来看看按位异或的概念:两数的同一位相同为0,相异为1

3与2的的一位不同,所以(3^2)的第一位为1,其他位都相同所以为0。

再来看这道题,想要使得(x^a)+(x^b)最小,那么肯定是每一位上尽可能都为0 

对于每一位,a,b这一位相同,那么x这一位也与它们相同即可。这样(x^a)与(x^b)这一位都为0。但是如果a,b这一位不同的话,因为只有0,1两种状态。所以(x^a)与(x^b)这一位必然有一个是1,有一个是0。因此如果判断a与b的每一位,如果这一位两个数不一样,那么结果不得不为1,如果两数一样,我们可以通过调整x这一位的数使其为0或者1,因为我们想要结果最小所以让其为0就行。 

#include<stdio.h>
int a,b;
void solve()
{
	int i,v=0;
	scanf("%d %d",&a,&b);
    for(i=0;a>>i||b>>i;i++)
    {
    	if( ((a>>i)&1)!=((b>>i)&1) )//判断第从右往左第i位是否相同
    	{
    	v+=(1<<i);//如果相同,那么这一位上结果为1
        }
    }
    printf("%d\n",v);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	solve();
	return 0;
}

解释一下代码部分:

如果我们想让一个数的原本为0的第i位为1,那么这个数加上1向左移i位即可:v+=(i<<i).

判断一个数从右往作第一位是否为0还是1 让这个数&1,因为&是都为1才为1,否则为0。因为1只有第0位也就是从右往左第一位为1,所以x&1,除了第0位可能为1,其他位一定为0。

那么判断两数的第i位是否相同呢?

让这个右移i位 再分别&1判断相不相同即可。

 01001右移2位变为010,00101右移两位变为001。在2进制下(010)&1为0,(001)&1为1。所以a,b第二位不同。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值