19年牛客多校第六场记录
A
- 题意: 垃圾分类
- 思路: 模拟
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define ll long long
#define FOR(i,l,r) for(int i = l ; i <= r ;++i )
#define inf 1<<30
#define EPS (1e-9)
#define ALL(T) T.begin(),T.end()
#define lson(i) i<<1
#define rson(i) (i<<1|1)
using namespace std;
typedef pair<int,int> pii;
int cnt[10];
char buf[3010];
char ma[1010];
int main(){
int t;
cin >> t;
FOR(cas,1,t){
scanf("%s%s",buf,ma);
cnt[1] = cnt[2] = cnt[3] = 0;
int len = strlen(buf);
for(int i=0;i<len;++i){
switch (ma[buf[i]-'a'])
{
case 'h':
cnt[1]++;
break;
case 'w':
cnt[2]++;
break;
case 'd':
cnt[3]++;
break;
default:
break;
}
}
printf("Case #%d: ",cas);
if(cnt[1]>=(len/4.0)){
printf("Harmful\n");
}else if(cnt[1]<=(len/10.0)){
printf("Recyclable\n");
}else if(cnt[3]>=cnt[2]*2){
printf("Dry\n");
}else{
printf("Wet\n");
}
}
return 0;
}
除数要double不然会wa QAQ
B
- 题意: 将2进制的ipv6地址转换成最短的字典序最小的标准格式(需要缩0)
- 思路: 模拟,先转成16进制,再去掉前置0,最后遍历所有缩0情况,获得答案.
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define ll long long
#define FOR(i,l,r) for(int i = l ; i <= r ;++i )
#define inf 1<<30
#define EPS (1e-9)
#define ALL(T) T.begin(),T.end()
#define lson(i) i<<1
#define rson(i) (i<<1|1)
using namespace std;
typedef pair<int,int> pii;
char buf[1029];
char ox[110];
char ox2[110];
char ansStr[110];
void toOx(){ // 变成16进制
int idx = 0;
int val = 0;
int oidx = 0;
while(idx<128){
val= (val<<1)+(buf[idx++]-'0');
val= (val<<1)+(buf[idx++]-'0');
val= (val<<1)+(buf[idx++]-'0');
val= (val<<1)+(buf[idx++]-'0');
if(val<10){
ox[oidx] = val+'0';
}else{
ox[oidx] = val-10+'a';
}
oidx++;
val = 0;
}
ox[oidx] = 0;
oidx = 0;
FOR(i,0,31){
ox2[oidx++] = ox[i];
}
ox2[oidx] = 0;
idx = oidx;
oidx = 0;
FOR(i,0,idx-1){
if(ox2[i]=='0'){
if(ox2[i+1]=='0'){
if(ox2[i+2]=='0'){
ox[oidx++] = ox2[i+3];
ox[oidx++] = ':';
i+=3;
}else{
ox[oidx++] = ox2[i+2];
ox[oidx++] = ox2[i+3];
ox[oidx++] = ':';
i+=3;
}
}else{
ox[oidx++] = ox2[i+1];
ox[oidx++] = ox2[i+2];
ox[oidx++] = ox2[i+3];
ox[oidx++] = ':';
i+=3;
}
}else{
ox[oidx++] = ox2[i];
ox[oidx++] = ox2[i+1];
ox[oidx++] = ox2[i+2];
ox[oidx++] = ox2[i+3];
ox[oidx++] = ':';
i+=3;
}
}
ox[oidx-1] = 0;
// printf("%s\n%s\n",ox2,ox);
}
int cmp(string s1,string s2){
if(s1.length() == s2.length()){
return s1<s2;
}
return s1.length() <s2.length();
}
vector<string> as;
void ya0(){ // 压缩0
vector<pii > pos;
int len = strlen(ox);
pii lin;
FOR(i,0,len-1){
if(ox[i]=='0'&&ox[i+1]==':'&&(i-1<0 ||ox[i-1]==':')){
lin.second = i;
lin.first = 0;
while(i+2<len&&ox[i+2]=='0'){
i+=2;
lin.first++;
}
pos.push_back(lin);
}
}
int idx= 0;
sort(ALL(pos));
if(pos.size()==0 || pos[pos.size()-1].first==0){
FOR(i,0,len){
ansStr[i] = ox[i];
}
as.push_back(ansStr);
return ;
}
int tmp = pos[pos.size()-1].first;
for(auto lin:pos){
if(lin.first!=tmp) continue;
idx = 0;
FOR(i,0,len-1){
if(i!=lin.second){
ansStr[idx++] = ox[i];
}else{
if(ansStr[idx-1]!=':')
ansStr[idx++] = ':';
ansStr[idx++] = ':';
i += lin.first*2+1;
}
}
ansStr[idx] = 0;
// string tstr = ansStr;
as.push_back(ansStr);
}
sort(ALL(as),cmp);
}
int main(){
int t;
scanf("%d",&t);
FOR(cas,1,t){
as.clear();
scanf("%s",buf);
toOx();
ya0();
printf("Case #%d: ",cas);
cout << as[0] << '\n';
}
return 0;
}
压中间的零和压首尾的零,字符串长度不一样. 直接遍历所有情况sort一遍即可
D
- 题意: 有k个箱子,n个物品,每个物品有一个体积\(a_i\). 对于每个箱子都要放到不能再放物品为止再去下一个箱子放.求最小的能放下所有物品的箱子容量
- 思路: 从下界\(\frac{\sum{a_i}}{n}\)开始枚举箱子的大小,multiset的upper_bound(第一个大于的)来模拟放的过程
#include<bits/stdc++.h>
#define ll long long
#define FOR(i,l,r) for(int i = l ; i <= r ;++i )
#define inf 1<<30
#define EPS (1e-9)
#define ALL(T) T.begin(),T.end()
#define lson(i) i<<1
#define rson(i) (i<<1|1)
using namespace std;
const int maxn(1e4+5);
int a[maxn],r,l;
int n,k;
void input(){
memset(a,0,sizeof(a));
scanf("%d%d",&n,&k);
r = 0;
FOR(i,1,n){
scanf("%d",&l);
r += l;
a[l]++;
}
r/=k;
}
multiset<int> s;
int check(int mid){
s.clear();
for(int i=1;i<=1000;++i){
for(int j=1;j<=a[i];++j){
s.insert(i);
}
}
for(int i=1;i<=k;++i){
int y = mid;
while(y){
auto iter = s.upper_bound(y);
if(iter != s.begin()){
--iter;
y -=(*iter);
s.erase(iter);
}else{
break;
}
}
}
return s.size()==0;
}
int main(){
int t;
scanf("%d",&t);
FOR(cas,1,t){
input();
while(!check(r)){
r++;
}
printf("Case #%d: %d\n",cas,r);
}
return 0;
}
这道题很容易想到二分,从而想到\(O(n^2)\)的\(check()\)+\(O(log(T))\)的二分,但这种取值不连续的填满问题是不满足二分的性质的... 可以通过每次枚举一段区间来二分.
有一道非常相似的题目U33405 纽约
G
- 题意: 给出n个字母\(A到J\)的字符串,求是否有一个\(0到9\)的排列能将所有字符串映射为一个合法的日期且为星期五.
- 思路: 先预处理出所有的排列,然后枚举通过蔡勒公式判断是否合法
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define ll long long
#define FOR(i,l,r) for(int i = l ; i <= r ;++i )
#define inf 1<<30
#define EPS (1e-9)
#define ALL(T) T.begin(),T.end()
#define lson(i) i<<1
#define rson(i) (i<<1|1)
using namespace std;
typedef pair<int,int> pii;
const int maxn = 1e6+10;
int mon[15] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int whatDay(int d,int m,int y){
int ans;
if(m==1||m==2)
m+=12,y--;
ans = (d+2*m+3*(m+1)/5 + y + y/4 - y/100 + y/400 + 1)%7;
return ans == 5;
}
int check(string op,int* a){
int y = a[op[0]-'A']*1000 + a[op[1]-'A']*100 + a[op[2]-'A']*10 + a[op[3]-'A'];
int m = a[op[5]-'A']*10 + a[op[6]-'A'];
int d = a[op[8]-'A']*10 + a[op[9]-'A'];
if(y%4==0 && y%100 || y%400==0) mon[2] = 29;
else mon[2] = 28;
if(y < 1600 || y>9999 || m<1 || m>12 || d<1 || d>mon[m]) return 0;
return whatDay(d,m,y);
}
int p[3628800][10];
void init(){
int a[10];
for(int i=0;i<10;++i)
a[i] = i;
int cnt = 0;
do{
memcpy(p[cnt++],a,sizeof(a));
}while(next_permutation(a,a+10));
}
string buf[maxn];
vector<int> tr;
int main(){
int t,n;
init();
cin >> t;
FOR(cas,1,t){
cin >> n;
FOR(i,1,n){
cin >> buf[i];
}
sort(buf+1,buf+n);
n = unique(buf+1,buf+n+1)-buf-1;
random_shuffle(buf+1,buf+n+1);
printf("Case #%d: ",cas);
int ok = 0;
FOR(i,0,3628800-1){
ok = 1;
FOR(j,1,n){
if(!check(buf[j],p[i])){
ok = 0;
break;
}
}
if(ok){
// tr.push_back(i);
FOR(j,0,9){
printf("%d",p[i][j]);
}
break;
}
}
if(!ok){
printf("Impossible");
}printf("\n");
}
return 0;
}
J
- 题意: 给出n*m的矩阵,每列的元素要从后往前选,每行全选会得到一个奖励,求能获得的最大收益.
- 思路: 枚举j列为所选最短的列(获得奖励),再枚举每一行的最大收益(从当前选到最后)
#include<bits/stdc++.h>
#define ll long long
#define FOR(i,l,r) for(int i = l ; i <= r ;++i )
#define inf 1<<30
#define EPS (1e-9)
#define ALL(T) T.begin(),T.end()
#define lson(i) i<<1
#define rson(i) (i<<1|1)
using namespace std;
typedef pair<int,int> pii;
const int maxn = 1e4+10;
ll c[maxn][maxn],d[maxn],mx[maxn][maxn],smx[maxn];
int n,m;
int main(){
int t;
scanf("%d",&t);
FOR(cas,1,t){
scanf("%d%d",&n,&m);
int x;
FOR(i,1,n){
FOR(j,1,m){
scanf("%d",&x);
c[i][j]=c[i][j-1]-x;
}
mx[i][m+1] = -1ll<<60;
for(int j=m;j>=0;--j){
mx[i][j] = max(mx[i][j+1],c[i][j]);
}
}
FOR(j,0,m){
smx[j] = 0;
FOR(i,1,n){
smx[j] += mx[i][j];
}
}
ll ans = 0,D=0,sum;
for(int j=0;j<=m;++j){
if(j) scanf("%d",&x),D+=x;
sum = -1ll<<60;
for(int i=1;i<=n;++i){
// if(c[i][j]-mx[i][j] > sum){
// sum = c[i][j] - mx[i][j];
// }
sum = c[i][j] - mx[i][j] + smx[j];
ans = max(ans,sum+D);
}
// sum+= smx[j];
// for(int i=1;i<=n;++i) sum+= mx[i][j];
// ans = max(ans,sum+D);
}
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}