2019牛客暑期多校训练营(第六场)
// 比赛链接:https://ac.nowcoder.com/acm/contest/886#question
// 又是自闭的一下午,今天我爆零了T_T // 开始直奔B题,一看感觉很容易,进制转换然后处理一下输出就完事了,签到题吧 // 喝完药就开始码,队友说A题签到,然后A了。我继续,看榜感觉不太对劲,B通过率真低。 // 写着突然B题更新,没问题,描述更清晰了而已,没有曲解题意,继续写。调好了样例,直接交。 // WA1 。。。WA2。。。 WA3。。。甩锅不写了 // 吴先生在写D题写半天了,我瞅一眼不就二分,我也写好了,互相交全WA。 // 吴先生一看输出都写错了,没敢交了。我自闭。 // 剩下时间看了G,想到那个求星期的公式,然后构思怎么枚举,断断续续写了快一个小时。 // 脑子有点不清晰,不知道用循环写还是dfs搜,反反复复,又觉得复杂度太高,怎么都要T。 // 陷入B-D-G死循环。
A - Garbage Classification
题意:给了一堆垃圾,判断它是有害垃圾/可回收垃圾/干垃圾/湿垃圾。两行输入,一行字符串表示垃圾,另一行代表每个小写字母的垃圾种类。
题解:模拟。真正的签到题。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; char s[3010],t[30]; int main() { int T,cas=0;cin>>T; while(T--) { printf("Case #%d: ",++cas); cin>>s>>t; int h=0,d=0,w=0; int ls = strlen(s); for(int i=0;i<ls;++i) { if(t[s[i]-'a']=='h') h++; else if(t[s[i]-'a']=='d') d++; else if(t[s[i]-'a']=='w') w++; } if(4*h>=ls) puts("Harmful"); else if(h*10<=ls) puts("Recyclable"); else if(d>=2*w) puts("Dry"); else puts("Wet"); } return 0; }
B - Shorten IPv6 Address (补)
题意:给一段ipv6的二进制地址,让你转成16进制输出,如果有至少两段的连续0,可以省写为两个冒号::,但只能用一次。求缩写最短的形式中字典序最小的答案。
思路:字典序最小,首先保证串最短,那么找到连续0最长的部分改写为【::】的即可,长度相等选择靠后的。
坑:首尾位置与中间位置加上【::】的长度不相同。。。
题解:官方题解不知道在说什么,看了直播也没听懂在讲什么,有一条弹幕给了一个【将最长的靠后0串改为::】的反例,我百思不得其解,正准备让电脑跑一下他们的字典序大小,写一起才看明白了:
【::】放在开头或结尾长度会比放中间多一位!!!
AC代码:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; char s[130]; int v[10]; int z[10]; int main() { int T; cin>>T; int t = 0; while(t<T) { scanf("%s", s+1); memset(z, 0, sizeof(z)); printf("Case #%d: ", ++t); int value = 0; for(int i=1;s[i];i++) { value = value*2 + (s[i]=='1'); if(i%16==0) { v[i/16] = value; value = 0; } } for(int i=8;i>=1;i--) { if(v[i]==0) { z[i] = z[i+1] + 1; z[i+1] = 0; } else z[i] = 0; } if(z[1]==8) { printf("::\n"); continue; } int len = 0, cnt = 0; int maxLen = -1, pos = -1; for(int i=1;i<=8;i++) { if(v[i]==0) { ++len; } else { if(len>=2) { ++cnt; if(maxLen<=len) { maxLen = len; pos = i-1; } } len = 0; } if(i==8 && len>=2) { ++cnt; if(maxLen<len || maxLen==len && maxLen==z[1] && cnt<=2) { maxLen = len; pos = i; } } } if(cnt<1) { for(int i=1;i<=8;i++) { printf("%0x%c", v[i], i==8?'\n':':'); } } else { int k = pos; while(v[k]==0 && k>=1) k--; if(k==0) printf(":"); for(int i=1;i<=k;i++) { printf("%0x:", v[i]); } printf(":"); if(pos==8) { printf("\n"); continue; } for(int i=pos+1;i<=8;i++) printf("%0x%c", v[i], i==8?'\n':':'); } } return 0; }
D - Move(补)
题意:有n个物品,每个体积vi,给你k个相同容积的箱子装,装箱按照从大到小顺序直到塞满或装不下为止。问箱子的最小容积。
思路:不能二分。。。因为箱子容积和k之间不满足单调性(即使经验上一致,也没找到反例)
官方反例:
n = 15 k = 5
39 39 39 39 39 60 60 60 60 60 100 100 100 100 100
199为合法的答案,但200不是,201也不是。
AC代码:(把二分改成暴力从 max(totV/k, maxV)开始枚举就够了。。)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int v[1010], n, k; bool vis[1010]; bool ok(int V) { int res = 0, cnt = 0; int now = V; memset(vis, 0, sizeof(vis)); while(cnt<n) { for(int i=n;i>=1&&now;i--) if(!vis[i] && now>=v[i]) { now -= v[i]; vis[i] = 1; ++cnt; } res++; now = V; } return res<=k; } int main() { int T; cin>>T; int t = 0; while(t<T) { scanf("%d %d", &n, &k); int totV = 0, maxV = 0; for(int i=1;i<=n;i++) { scanf("%d", &v[i]); totV += v[i]; maxV = max(maxV, v[i]); } sort(v+1, v+1+n); int ans = max(maxV, (totV+k-1)/k); while(!ok(ans)) { ++ans; } printf("Case #%d: %d\n", ++t, ans); } return 0; }
G - Is Today Friday?(补)
题意:给你n个只包含A~J大写字母的yyyy/mm/dd形式的加密字符串,求解A~J使日期都是星期五。(n<=10000)
思路:每个日期枚举的规模在:2(月开头数字) * 4(日开头数字) * 8 * 7 * 6 * 5 * 4 * 3 = 1.6e5,然后再判断n个是否满足。
题目数据sb。。。知道蔡勒公式再暴力枚举就完事了。。。关键在于去重。。。
sort(date+1, date+1+n);
n = unique(date+1, date+1+n) - (date+1);
AC代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct node { char y[5]; char m[3]; char d[3]; string s; bool operator<(const node& a) { return s < a.s; } bool operator==(const node& a) { return s == a.s; } }date[100100]; int n; bool valid(int y, int m, int d) { if(m>12 || d>31) return false; if(m==2) { if(d<=28) return true; else if(d>29) return false; return y%400==0 || (y%4==0 && y%100!=0); } if(d==31) { switch(m) { // case 1: case 3: case 5: case 7: case 8: // case 10: case 12: return true; case 4: case 6: case 9: case 11: return false; return true; } } return true; } bool check(int y, int m, int d) { // 蔡勒公式 w为星期,0表示星期天 if(m==1 || m==2) m += 12, --y; int c = y / 100; y = y - c * 100; int w = y + y / 4 + c / 4 - 2 * c + 26 * (m + 1) / 10 + d - 1; w = (w%7+7)%7; return w==5; } bool vis[10], flag; int dic[10]; int month_First[10]; int day_First[10]; void dfs(int k) { if(k>9) { for(int i=1;i<=n;i++) { int year = dic[date[i].y[0]-'A']*1000 + dic[date[i].y[1]-'A']*100 + dic[date[i].y[2]-'A']*10 + dic[date[i].y[3]-'A']; if(year<1600) return; int mon = dic[date[i].m[0]-'A']*10 + dic[date[i].m[1]-'A']; int day = dic[date[i].d[0]-'A']*10 + dic[date[i].d[1]-'A']; if(!valid(year, mon, day) || !check(year, mon, day)) return; } flag = true; for(int i=0;i<=9;i++) { printf("%d", dic[i]); } printf("\n"); return; } for(int i=0;i<=9;i++) { if(i>2 && month_First[k]) break; if(i>3 && day_First[k]) break; if(!vis[i]) { dic[k] = i; vis[i] = 1; if(!flag) dfs(k+1); vis[i] = 0; dic[k] = -1; } } } int main() { int T; cin>>T; int t=0; while(t<T) { scanf("%d", &n); memset(month_First, 0, sizeof(month_First)); memset(day_First, 0, sizeof(day_First)); for(int i=1;i<=n;i++) { scanf("%4s/%2s/%2s", date[i].y, date[i].m, date[i].d); date[i].s = date[i].y; date[i].s += date[i].m; date[i].s += date[i].d; month_First[date[i].m[0]-'A'] = 1; day_First[date[i].d[0]-'A'] = 1; } sort(date+1, date+1+n); n = unique(date+1, date+1+n) - (date+1); printf("Case #%d: ", ++t); flag = 0; dfs(0); if(!flag) printf("Impossible\n"); } return 0; }
J - Upgrading Technology
(未完待补。。。)