A题
题目大意:有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题
题目大意:给定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题
题目大意:给定一个长度为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题
题目大意: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第二位不同。