2019牛客暑期多校训练营(第六场)
参考资料:
[1]:官方题解(提取码:bdv1)
B.Shorten IPv6 Address(模拟)
•题意
已知 ip 地址由 8 位十六进制的数组成;
例如 ip = 0000:0000:0123:4567:89ab:0000:0000:0000;
现在让你按照如下规则化简这个 ip 地址:
(1):前导 0 去掉,例如上述 ip 地址可化简为 ip1 = 0:0:123:4567:89ab:0:0:0;
(2):连续的 0 字段用 ':' 代替,但只能替换一次,例如,在 ip1 的基础上再进行化简;
ip2 = ::0123:4567:89ab:0000:0000:0000 或 ip2 = 0000:0000:0123:4567:89ab::;
(3):再长度最短的情况下使得字典序最小,例如,上述 ip 地址化简中有两个 ip2,取字典序最小的
ip3 = 0000:0000:0123:4567:89ab:: ;
给你 128 位二进制表示的字符串,输出简化后的 ip 地址(转化成十六进制);
•题解
按照题意模拟即可;
我是先将二进制数转化成十进制数进行化简操作,然后输出十六进制的 ip 地址;
需要注意的坑点,假设某化简前的 ip = 0:0:1:0:0:1:0:0;
将中间的两个 0 字段用 ':' 代替比将末尾的两个 0 字段用 ':' 代替长度更短;
所以此 ip 地址化简后的结果位 ip = 0:0:1::1:0:0;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INFll 0x3f3f3f3f3f3f3f3f 4 #define ll long long 5 const int maxn=200; 6 7 char s[maxn]; 8 int ip[20]; 9 vector<int >p[20];///p[i]:连续的i个0字段的起始位置 10 char ch[20]="0123456789abcdef"; 11 12 int f(int j)///二进制转十进制 13 { 14 int ans=0; 15 int cnt=0; 16 for(int i=j+15;i >= j;--i) 17 { 18 if(s[i] == '1') 19 ans += (1<<cnt); 20 21 cnt++; 22 } 23 return ans; 24 } 25 void OX(int v)///十进制转十六进制 26 { 27 if(v/16 == 0) 28 { 29 printf("%c",ch[v%16]); 30 return ; 31 } 32 OX(v/16); 33 34 printf("%c",ch[v%16]); 35 } 36 void Solve() 37 { 38 int k=0; 39 for(int i=1;k < 8;i += 16) 40 ip[++k]=f(i); 41 42 for(int i=0;i <= 8;++i) 43 p[i].clear(); 44 45 int index=1; 46 while(index <= k)///查找连续的0位置 47 { 48 for(;index <= k && ip[index] != 0;index++); 49 50 int cnt=0; 51 int cur=index; 52 53 for(;index <= k && ip[index] == 0;cnt++,index++); 54 p[cnt].push_back(cur); 55 } 56 57 for(int i=k;i > 1;--i)///从大到小查找大于两个字段的0位置 58 { 59 if(p[i].size() == 0) 60 continue; 61 62 int cur=p[i][p[i].size()-1]; 63 ///判断cur是否为末尾的连续的0,并且判断中间是否含有相同长度的0字段 64 if(p[i].size() > 1 && cur+i == 9 && p[i][p[i].size()-2] != 1) 65 cur=p[i][p[i].size()-2]; 66 67 for(int j=1;j < cur;++j) 68 { 69 OX(ip[j]); 70 if(j != cur-1) 71 printf(":"); 72 } 73 printf("::"); 74 for(int j=cur+i;j <= k;++j) 75 { 76 OX(ip[j]); 77 if(j != k) 78 printf(":"); 79 } 80 printf("\n"); 81 return ; 82 } 83 84 for(int i=1;i <= k;++i) 85 { 86 OX(ip[i]); 87 if(i != k) 88 printf(":"); 89 } 90 printf("\n"); 91 } 92 93 int main() 94 { 95 //freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin); 96 int T; 97 scanf("%d",&T); 98 for(int kase=1;kase <= T;++kase) 99 { 100 scanf("%s",s+1); 101 102 printf("Case #%d: ",kase); 103 Solve(); 104 } 105 return 0; 106 }
D.Move(假的二分)
•题意
有 n 个物品,第 i 个物品体积为 vi;
有 k 个容积相同箱子装这 n 个物品,按照如下策略装:
将尽可能大的物品装箱;
•题解(by官方)
对上界为 $\lceil\frac{sum}{k}\rceil+maxV$ 的理解;
首先,如果可以将物品拆卸,那么 $\lceil\frac{sum}{k}\rceil$ 是可以将所有物品都装下的最小整数解;
那么,因为每个箱子的容积都增加了 maxV;
那么,对于某个被拆分的物品,便可通过增加的体积使得这个物品装入一个箱子;
所以, $\lceil\frac{sum}{k}\rceil+maxV$ 为一个合法的上界;
这样的话,只需遍历 maxV 次即可找出答案,而 maxV ≤ 1000;
•Code(multiset+upper_bound)
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e3+50; 4 5 int n,k; 6 int v[maxn]; 7 8 bool Check(int V) 9 { 10 multiset<int >_set; 11 for(int i=1;i <= n;++i) 12 _set.insert(v[i]); 13 14 for(int i=1;i <= k;++i) 15 { 16 int cur=V; 17 while(cur && !_set.empty()) 18 { 19 auto it=_set.upper_bound(cur); 20 if(it == _set.begin()) 21 break; 22 23 it--; 24 cur -= *it; 25 _set.erase(it); 26 } 27 } 28 return _set.empty() ? true:false; 29 } 30 int Solve() 31 { 32 int sum=0; 33 for(int i=1;i <= n;++i) 34 sum += v[i]; 35 36 for(int i=(sum+k-1)/k;;++i) 37 if(Check(i)) 38 return i; 39 } 40 int main() 41 { 42 int T; 43 scanf("%d",&T); 44 for(int _T=1;_T <= T;++_T) 45 { 46 scanf("%d%d",&n,&k); 47 for(int i=1;i <= n;++i) 48 scanf("%d",v+i); 49 50 printf("Case #%d: %d\n",_T,Solve()); 51 } 52 return 0; 53 }
J.Upgrading Technology(贪心+优先级队列)
•题意
有 n 种技能,每种技能的最大等级为 m;
初始,这 n 种技能的等级全部为 0;
定义 $c_{i,j}$ 表示第 i 种技能从等级 j-1 升级为等级 j 需要消耗 $c_{i,j}$ 的能量;
但是,如果 $c_{i,j}<0$,则表示从中获得 $\left|c_{i,j}\right|$ 的能量;
定义 $d_{i}$ 表示全部技能都升级到 i 级所获得的能量;
但是,如果 $d_{i}<0$ 则表示消耗 $\left|d_{i}\right|$ 的能量;
每种技能可以升级到 0~m 任意等级,求最终获得的最大能量是多少;
•题解
枚举等级 j;
判断只使用 d1,d2,...,dj 所获得的能量;
和至少有一种技能最高等级为 j 时(保证只使用d1,d2,...,dj),其他技能通过 $c_{i,j}<0$ 所获得的最大能量;
答案取最大值即可;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INFll 0x3f3f3f3f3f3f3f3f 4 #define ll long long 5 const int maxn=1e3+50; 6 7 int n,m; 8 ll c[maxn][maxn]; 9 ll d[maxn]; 10 ll sum[maxn][maxn];///sum[i][j]:第i种技能从0升级到j需要的总消耗 11 ll Min[maxn][maxn];///Min[i][j]:第i种技能从0升级到j~m需要的总消耗的最小值 12 ll p[maxn];///p[i]:这n种技能从0升级到i所获得的能量 13 ll Max[maxn];///Max[i]:这n种技能中的任意少于n种的技能从i级升级到任意等级,从c中获得的最多的能量 14 priority_queue<ll ,vector<ll >,less<ll > >q; 15 16 ll Solve() 17 { 18 for(int i=1;i <= n;++i) 19 { 20 sum[i][0]=0; 21 for(int j=1;j <= m;++j) 22 sum[i][j]=sum[i][j-1]+c[i][j]; 23 } 24 for(int i=1;i <= n;++i) 25 { 26 Min[i][m+1]=INFll; 27 for(int j=m;j >= 1;--j) 28 Min[i][j]=min(Min[i][j+1],sum[i][j]); 29 } 30 p[0]=0; 31 for(int j=1;j <= m;++j) 32 { 33 ll cur=0; 34 for(int i=1;i <= n;++i) 35 cur += c[i][j]; 36 ///共消耗cur点能量,获得d[j]点能量 37 p[j]=p[j-1]+d[j]-cur; 38 } 39 40 Max[0]=Max[m+1]=0; 41 for(int j=1;j <= m;++j) 42 { 43 while(!q.empty()) 44 q.pop(); 45 Max[j]=0; 46 47 for(int i=1;i <= n;++i) 48 { 49 ll cur=Min[i][j]-sum[i][j-1]; 50 if(cur < 0) 51 q.push(-cur); 52 } 53 54 int cnt=0; 55 while(!q.empty()) 56 { 57 ll cur=q.top(); 58 q.pop(); 59 cnt++; 60 61 if(cnt < n)///取最多n-1个通过c获得的最大能量 62 Max[j] += cur; 63 } 64 } 65 ll ans=Max[1]; 66 67 for(int i=1;i <= m;++i) 68 ans=max(ans,p[i]+Max[i+1]); 69 return ans; 70 } 71 int main() 72 { 73 int T; 74 scanf("%d",&T); 75 for(int _T=1;_T <= T;++_T) 76 { 77 scanf("%d%d",&n,&m); 78 for(int i=1;i <= n;++i) 79 for(int j=1;j <= m;++j) 80 scanf("%lld",c[i]+j); 81 for(int i=1;i <= m;++i) 82 scanf("%lld",d+i); 83 84 printf("Case #%d: %lld\n",_T,Solve()); 85 } 86 return 0; 87 }