2019牛客暑期多校训练营(六)
声明:本文章题目来源均为牛客网
链接:https://ac.nowcoder.com/acm/contest/886#question
A-Garbage Classification
一道垃圾分类题(2333),签到就不写了。
B-Shorten IPv6 Address
题目类型:进制转换,思维模拟
题目链接:https://ac.nowcoder.com/acm/contest/886/B
题目大意
给一个01二进制字符串地址,一共有128个字符,然后确定它的最短表示:
以十六进制表示形式表示地址,并使用冒号 ' : ' 分割每四个十六进制数字。每四个数字称为一个字段,例如:
00000000000000000000000000000000000000010010001101000101011001111000100110101011000000000000000000000000000000000000000000000000
转化为下面的形式(这里的:是中文字符,为了方便演示,写代码的时候注意下中英文)
0000:0000:0123:4567:89ab:0000:0000:0000
可以省略字段中的前导零。例如上面的十六进制表示为:
0:0:123:4567:89ab:0:0:0
由至少两个字段组成的连续零字段(包括靠近它们的冒号)可以用双冒号'::'替换。此外,地址中不能使用多个双冒号。
例如,上面的IPv6地址可以缩短为0:0:123:4567:89ab ::或:: 123:4567:89ab:0:0:0,但不能缩写为:: 123:4567: 89ab ::。
如果有多个相同长度的最短格式,请使用字典(将缩短的IPv6地址视为字符串)最小的一个。
题目理解
二进制转换成十六进制的方法是,取四合一法,所以16个二进制数字可转换为4个十六进制数字,而c语言里有现成的16进制的输出方式(%x),
so,我们可以把128个字符里每16个字符表示的十进制数字用数组存起来,然后输出的时候用%x输出每个数字就行了(转换成数字还可以去掉前导零)。
不过还要考虑题目所说的最短的输出,所以还要考虑连续的数字0的最长长度。而且题目说了相同长度的串要求输出字典序最小的那一个,查一下ASCII表,发现' : '大于' 0 ',所以应该尽量输出最长长度0数字连续子串在中间的字符串或者靠后的。
比如0:0:123:4567:89ab ::或:: 123:4567:89ab:0:0:0应该输出第一个。
题目代码
#include<iostream>
#define sc(x) scanf("%d",&x); //方便输入
using namespace std;
int main(){
int t,x;sc(t);x=t;getchar();
while(t--){
int maxlen=0,len=0,pos=0,a[20];
for(int i=1;i<=8;i++){
a[i]=0;
for(int j=1;j<=16;j++){ //数字转换
int x=getchar()-'0';
a[i]=a[i]*2+x;
}
if(a[i]==0) len++; //连续0的长度
else{
if(len>=maxlen&&len>1){ //更新连续0的最大长度
maxlen=len;
pos=i-len;
}
len=0;
}
if(i==8&&len>1){ //如果中间连续零子串较短就选择靠后的
if(len>maxlen||(len==maxlen&&pos==1)){
pos=i-len+1;
maxlen=len;
}
}
}
// for(int i=1;i<=8;i++) printf("%d\n",a[i]);
printf("Case #%d: ",x-t);
if(pos==1) printf(":");
for(int i=1;i<=8;i++){
if(i==pos) printf(":"),i+=maxlen;
if(i>8) printf("\n");
else printf("%x%c",a[i],":\n"[i==8]);//if(i=8) 输出\n else 输出 :
}
}
return 0;
}
/*
题目样例
3
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000010010001101000101011001111000100110101011000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000010010001100000000000000000000000000000000000000000000000001000101011001111000100110101011
*/
后记
这题题目看懂了就好做了,模拟题一般需要看懂题目。
GIs Today Friday?
题目类型:模拟,全排列
题目链接:https://ac.nowcoder.com/acm/contest/886/G
题目大意
唐唐喜欢星期五,他做了一个日期表,里面的日期全是星期五。这个列表里的每个日期形式为“ yyyy/mm/dd ”,其中“ yyyy ”是一个四位数代表年份的数字,“ mm ”是代表月份的两位数字,“ dd ”是代表日期的两位数字。这个日期表里只有1600年到9999年之间的年分,月份与日期前面可能会有前导零。为了安全存储,唐唐用A~J的十个字母替换了日期表中0~9的十个数字。每个字母都对应不同的数字。然而唐唐是个虎娃,前脚加了密,后脚就忘记了。于是请求身为大佬的你,找到A~J对应的0~9的数字表。(对应关系要符合日期为星期五的要求)
题目理解
题目已经说的很清楚,找到一种数字排列使得对应字母表示的日期为星期五。所以可以将0~9数字的排列枚举一下(可以用c++里的全排列函数next_permutation),找到一种对应关系然后再用一些判断星期的方法来判断是否符合要求。判断日期可以用蔡勒公式或者基姆拉尔森计算公式。注意判断年份是否在1600年到9999年之间等要求。
AC代码
#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define sc(x) scanf("%d",&x);
#define scll(x) scanf("%lld",&x);
int mon[13]={0,31,28,31,30,31,30,31,31,30,31,30,31},n;
int num[12]={0,1,2,3,4,5,6,7,8,9};
string s[100010];
bool judge(int y,int m,int d){
if((y%4==0 && y%100!=0) || y%400==0) mon[2]=29;
else mon[2]=28;
if(y<1600 || y>9999) return 1;
if(m<1 || m>12) return 1;
if(d<1 || d>mon[m]) return 1;
return 0;
}
bool zaller(int y,int m,int d){
//如果月份为1或2的话,要看成上一年的13或14月份
if(m==1 || m==2) m+=12,y--;
/* 基姆拉尔森计算公式
0~6 代表 星期一~星期日
*/
// return ((d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7+1)%7==5?1:0;
/* 蔡勒公式
1582年10月4日后用下面的公式
0~6 代表 星期日~星期六
*/
int c=y/100;y=y%100;
return ((y+y/4+c/4-2*c+26*(m+1)/10+d-1)%7+7)%7==5?1:0;
}
bool check(){
for(int i=0;i<n;i++){
int y=0,m=0,d=0;
y=num[(s[i][0]-'A')]*1000+num[(s[i][1]-'A')]*100+num[(s[i][2]-'A')]*10+num[(s[i][3]-'A')];
m=num[(s[i][5]-'A')]*10+num[(s[i][6]-'A')];
d=num[(s[i][8]-'A')]*10+num[(s[i][9]-'A')];
if(judge(y,m,d)) return 0;
if(!zaller(y,m,d)) return 0;
}
return 1;
}
int main(){
int x,t;sc(t);x=t;
while(t--){
sc(n);
for(int i=0;i<n;i++) sc(s[i]);
sort(s,s+n);
n=unique(s,s+n)-s;//字符串去重
for(int i=0;i<10;i++) num[i]=i;
bool flag=0;
printf("Case #%d: ",x-t);
do{
if(check()){
flag=1;
for(int j=0;j<10;j++) printf("%d",num[j]);
printf("\n");
break;
}
}while(next_permutation(num,num+10));
if(!flag) printf("Impossible\n");
}
return 0;
}
后记
题目不难,题目看懂了不知道公式直接百度就行了。