Digit Blocks-Google Codejam 2021 Round 1B 第三题
You are going to build N towers of B cubic blocks each, one block at a time. Towers are built bottom-up: the i-th block to be placed in a tower ends up as the i-th from the bottom. You need to decide where to place each block before getting to see any of the upcoming blocks, and once placed, blocks cannot be moved.
Each block has a single decimal digit printed on it, and towers are built such that the faces with digits are all facing the front. The font is such that blocks cannot be rotated to obtain a different digit (for example, a block with a 6 on it cannot be rotated to obtain a block with a 9 on it, nor vice versa).
After the building is done, we read the B digit integer printed on the front of each tower from the top to the bottom (that is, the digit on the last block placed on a tower is the most significant digit). Notice that these integers may have any number of leading zeroes. Then, we add those N integers together to obtain the score of our building operation.
The digit for each block is generated uniformly at random, and independently of any other information. In order for your solution to be judged correct, the sum of its scores over all T test cases must be at least P.
题目大意:
这是一道交互题。你需要建造N个高度为B的高塔。每次系统会生成一个印有随机数字(0到9)的方块,你需要将它摆放到这N堆中。摆放完毕后不能移动方块,你不知道之后方块上的数字。你需要让这N座高塔上的数(从上往下读数字形成的B位数)和尽可能大(至少为P,P约为98%期望最大值)。
思路解析:
这是一道工程题,与一般算法竞赛题目不同,这道题不怎么注重算法和复杂度,更看重策略。注意到数据保证随机,也就是说每种数字的个数应当是大致相同的,一个很显然的想法是尽量让每一层最上方的两个数字尽可能大(即做到最大),因为之后的数字对总和的贡献仅仅为1%左右,是题目允许的误差。那么如何保证我们最上方的数字最优呢?
每当我们收到一个新的方块,如果它是9,那么我们把它放到当前未完成的最高的塔上;如果是8,我们用一个类似于模拟退火中使用的跳跃函数来决定是把它放在什么高度的塔上(重点是带有权重的随机性,几率应当与塔的高度正相关,用什么函数其实不重要);否则,我们把它随机放到其他塔上,尽量保证塔的高度从高到低均匀分配(这样下次来9和8时不至于没有高塔放)。这种方法显然是偏骗分的,不过在这种题中还是挺常见的,这道题也够用了。
我们也可以考虑dp的思想,对于马上要建完的塔(高度至少为B-2),我们用dp计算把下一个方块放在这些塔上的期望和(考虑下一个方块0~9的可能性再平均),由于我们只考虑每座塔的最后两个位置,其他位置都仍然用之前的贪心方法解决,所以总共状态数不多。
这道题应当还有很多其他方法(因为这种题本来也很难有严谨的解法),在此就不一一赘述了。
下面的解法是最偷懒的(比赛中当然越省脑子越好),事实证明不需要随机,也不需要DP就可以凭运气过。
#include<bits/stdc++.h>
using namespace std;
define LL long long
LL P;
int T, N, B;
vector<int>vv;
int main(){
scanf("%d%d%d%lld",&T,&N,&B,&P);
for(int t = 1; t <= T; t += 1)
{
vv.clear();
for(int i = 1; i<=N + 1 ; i+=1)vv.push_back(0);
for(int i = 1; i <= N * B; i += 1)
{
int x,j = 0;
scanf("%d",&x);
if(x < 8)//直接放入矮的地方
{
if(!j)
{
for(int i = 1; i <= N; i += 1)//求稳可以随机
{
if(vv[i] <= B - 3)
{
j = i;
break;
}
}
}
if(!j)//没有矮的只能放更高的
{
for(int i = 1; i <= N; i += 1)
{
if(vv[i] == B - 2)
{
j = i;
break;
}
}
}
if(!j)//只能填满
{
for(int i = 1; i <= N; i += 1)
{
if(vv[i] == B - 1)
{
j = i;
break;
}
}
}
}
else if(x == 8)//尽量放倒数第二层,其余同理
{
if(!j)
{
for(int i = 1; i <= N; i += 1)
{
if(vv[i] == B - 2)
{
j = i;
break;
}
}
}
if(!j)
{
for(int i = 1; i <= N; i += 1)
{
if(vv[i] <= B - 3)
{
j = i;
break;
}
}
}
if(!j)
{
for(int i = 1; i <= N; i += 1)
{
if(vv[i] == B - 1)
{
j = i;
break;
}
}
}
}
else//尽量放最高层
{
if(!j)
{
for(int i = 1; i <= N; i += 1)
{
if(vv[i] == B - 1)
{
j = i;
break;
}
}
}
if(!j)
{
for(int i = 1; i <= N; i += 1)
{
if(vv[i] == B - 2)
{
j = i;
break;
}
}
}
if(!j)
{
for(int i = 1; i <= N; i += 1)
{
if(vv[i] <= B - 3)
{
j = i;
break;
}
}
}
}
vv[j]++;
printf("%d\n",j);
}
}
return 0;
}