2019牛客暑期多校第六场题解ABDJ
A.Garbage Classification
题意:给你两个串,第一个串s由小写字母组成,第二个串t由dwh组成,长度为26,分别表示字母a到z代表的字符。现在要你判断:
- 如果字符串中‘h’的数量至少占s串长度的25%,输出 “Harmful”
- 如果字符串中‘h’的数量最多占s串长度的10%,输出 “Recyclable”
- 否则,如果字符串中‘d’的数量至少是‘w’的两倍,输出 “Dry”
- 否则输出 “Wet”
题解:判断即可。
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 2e3 + 10; char s[N],t[30]; int main() { int T, cnt = 1; for (scanf("%d",&T);T--;) { scanf("%s%s",s,t); int len = strlen(s); printf("Case #%d: ", cnt++); int num[30] = {0},w = 0, h = 0, d = 0; for (int i = 0; i < len; i++) num[s[i]-'a']++; for (int i = 0; i < 26; i++) { if (t[i] == 'w') w+=num[i]; if (t[i] == 'h') h+=num[i]; if (t[i] == 'd') d+=num[i]; } if (h*4 >= len) printf("Harmful\n"); else if (h*10 <= len) printf("Recyclable\n"); else if (d >= 2 * w) printf("Dry\n"); else printf("Wet\n"); } return 0; }
B.Shorten IPv6 Address
题意:给你一个128长度的二进制串,要转化成IPv6地址的形式,例如 “0:0:123:4567:89ab:0:0:0”(忽略前导0),若有连续两个及以上的0,那么可以把那一段写成“::”,但是注意一个地址中最多有1个“::”。要求输出最短的形式,如果长度相同输出字典序最小的结果。
题解:显然我们将最长连续0的那一段转化为“::”比转化把它短的要优,当有长度相同的连续0时,显然转化中间的比转化两边的优(转化中间的比转化两边的长度要少1),相同且都在首尾或者且都在中间时,显然选转化后面那个更优(‘0’字典序比‘:’小)。
可以将二进制转化为十六进制比较也可以转化为十进制比较,因为C++可以直接%x输出十六进制数所以用十进制比较方便。
想法是对的但是写丑了WA了一下午嘤嘤嘤(╥╯^╰╥)
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; #define maxn 2 const int N = 1e5 + 10; char s[200]; int cel(int id) { int x = 0; for (int i = id; i < id + 16; i++) x = x * 2 + s[i]-'0'; return x; } int main() { int T,t=1; for (scanf("%d",&T);T--;) { scanf("%s",s); int pos = -1,num = 1; int a[10] = {0}; for (int i = 0; i < 128; i+=16) a[i/16] = cel(i); for (int i = 7; i >= 0; i--) { int cnt = 0; if (!a[i]) { while(i>=0 && !a[i]) i--,cnt++; i++; if (cnt == num && pos+num == 8 && i) pos = i,num = cnt; if (cnt > num) pos = i,num = cnt; } } printf("Case #%d: ",t++); for (int i = 0; i < 8; i++) { if ( i == pos) { if (pos == 0) printf(":"); printf(":"); i+=num-1; }else { printf("%x",a[i]); if (i!=7) printf(":"); } } printf("\n"); } return 0; }
#include <bits/stdc++.h> #define ll long long using namespace std; #define maxn 2 const int N = 1e5 + 10; char s[200],ans[10][10]; bool _is0[10]; char cel(int id) { int x = 0; for (int i = id; i < id + 4; i++) x = x * 2 + s[i]-'0'; if (x < 10) return x+'0'; return x-10+'a'; } bool judge0(int x){ int i; for (i = 0; ans[x][i+1]; i++) { if (ans[x][i] != '0') return false; for(int j = i; ans[x][j]; j++) ans[x][j] = ans[x][j+1]; i--; } return ans[x][i]=='0'; } int main() { int T,t=1; for (scanf("%d",&T);T--;) { scanf("%s",s); int pos = -1; int num = 1; for (int i = 0, j = 0,k = 0; i < 128; i+=4) { ans[k][j++] = cel(i); if(j == 4) ans[k][j] = '\0',j = 0,_is0[k]=judge0(k),k++; } for (int i = 7; i >= 0; i--) { int cnt = 0; if (_is0[i]) { while(i>=0 && _is0[i]) i--,cnt++; i++; if (cnt == num && pos+num == 8 && i) pos = i,num = cnt; if (cnt > num) pos = i,num = cnt; } } printf("Case #%d: ",t++); for (int i = 0; i < 8; i++) { if ( i == pos) { i+=num-1; if (pos == 0) printf(":"); printf(":"); }else { printf("%s",ans[i]); if (i!=7) printf(":"); } } printf("\n"); } return 0; }
D.Move
题意:有n件物品,每件物品体积为vi,用k个相同大小的盒子来装他们,问盒子最小体积可以为多少。
题解:我们知道盒子最小体积为max(a[n],sum/k),我们可以从最小可能的体积开始枚举,一个个来判断。数据都很小不超过1000所以不会T。
那我们怎么判断呢?我们可以把物品按体积由大到小依次放进盒子里,能放得下就放,否则就放下一个,放不进就表示盒子小了,否则就是答案。
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 1e3 + 10; const ll INF = 1ll<<60; int a[N],n,m,b[N]; bool check(int x){ for (int i = 1; i <= m; i++) b[i] = x; for (int i = n; i >= 1; i--) { bool fg = 0; for (int j = 1; j <= m; j++) if (a[i] <= b[j]) { b[j] -= a[i]; fg = 1; break; } if (!fg) return false; } return true; } int main() { int T, cnt = 1; for (scanf("%d",&T);T--;) { int sum = 0; scanf("%d%d",&n,&m); for (int i = 1; i <= n; i++) { scanf("%d",&a[i]); sum+=a[i]; } sort(a+1,a+1+n); for (int i = max(a[n],sum/m);;i++) if (check(i)) { printf("Case #%d: %d\n", cnt++,i); break; } } return 0; }
J.Upgrading Technology
题意:有n个技能,每个技能都有m个等级(一开始等级都为0),每升一级需要花费C(i,j)(只能一级一级升),如果每个技能都达到i级,可以得到di的利润。可以不升级利润为0,。问能得到的最大利润为多少。
题解:我们可以用前缀和维护每个技能升级的最小花费,倒着更新一遍最小值。然后把每个技能升级到i(可能是以上)所需的最小花费加起来,然后我们维护下每个等级利润d的前缀和,技能最小等级为i时的利润为d的前缀和 - 每个技能升级到i所需的最小花费。这里要注意两个点,一是我们前面维护的每个技能升级到i(可能是以上)所需的最小花费可能把技能等级更新到了i以上,若每个技能等级都在i以上,那么获得的利润就不是d1~i,而是当前最小技能j的利润和d1~j,所以我们需要保留一个升级到i所需花费与我们维护的最小值差最小的来确保最低等级为i且获得的利润最大。二是注意可能存在有技能等级为0,其他技能升级花费为负的情况。
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 1e3 + 10; const ll INF = 1ll<<60; ll a[N][N],c[N][N],b[N]; int main() { int T, cnt = 1,n,m; for (scanf("%d",&T);T--;) { scanf("%d%d",&n,&m); memset(a,0,sizeof(a)); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { scanf("%lld",&a[i][j]); a[i][j] += a[i][j-1]; c[i][j] = a[i][j]; } a[n+1][m] += a[i][m]; for (int j = m-1; j >= 0; j--){ a[i][j] = min(a[i][j],a[i][j+1]); a[n+1][j] += a[i][j]; } } b[0] = 0; for (int i = 1; i <= m; i++) { scanf("%lld",&b[i]); b[i] += b[i-1]; } ll ans = 0; for (int i = 0; i <= m; i++) { int pos = -1; ll mind = INF,d; for (int j = 1; j <= n; j++) { d = c[j][i] - a[j][i]; if (d < mind) { mind = d; pos = j; } } ans = max(ans,b[i]-a[n+1][i]+a[pos][i]-c[pos][i]); } printf("Case #%d: %lld\n", cnt++,ans); } return 0; }