目录
2021年度训练联盟热身训练赛第七场(GCPC2020)
Problem A: Adolescent Architecture
题意
给定一堆圆柱和正方块,要求你把他们叠起来,保证从上往下每个方块都被下面的方块包含。、
思路
将正方体的边长* 2 / 2 \sqrt{2} / 2 2/2 和圆柱的半径作为权值,进行排序,在这两者值相等时正方体放在圆柱上面。排完序之后遍历看是否无解,只有一种情况会无解,就是下面是正方体上面是圆柱,尽管满足前面的权值排序,但是正方体边长/2比圆柱半径小,那也不行。
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 2e3 + 10;
const double eps = 1e-5;
struct node
{
double a;
int type; // 1 cube, 2 cylinder
bool operator < (const node& obj) const
{
if(type == obj.type)
return a < obj.a;
double r1, r2;
if(type == 1)
r1 = a * sqrt(2.0) / 2.0;
else
r1 = a;
if(obj.type == 1)
r2 = obj.a * sqrt(2.0) / 2.0;
else
r2 = obj.a;
if(r1 == r2)
return type < obj.type;
return r1 < r2;
}
};
priority_queue<node> que;
string tmp;
stack<node> ans;
int main()
{
int n;
cin >> n;
for(int i = 0, x; i < n; i++)
{
cin >> tmp >> x;
node y;
y.a = (double)x;
if(tmp[1] == 'u') // cube
y.type = 1;
else
y.type = 2;
que.push(y);
}
node now;
now.a = INF;
now.type = 2;
bool flag = 0;
while(!que.empty())
{
node nxt = que.top();
que.pop();
if(now.type == 1 && now.type != nxt.type)
{
if(nxt.a * 2.0 > now.a)
{
flag = 1;
break;
}
}
now.a = nxt.a;
now.type = nxt.type;
ans.push(nxt);
}
if(flag)
{
cout << "impossible" << endl;
return 0;
}
while(!ans.empty())
{
if(ans.top().type == 1)
cout << "cube ";
else
cout << "cylinder ";
cout << ans.top().a << endl;
ans.pop();
}
return 0;
}
Problem B:Bookshelf Building
题意
有 n n n 本高度宽度不同、深度相同的书,放入一个 h e i g h t : y w i d t h : x height:y \ width:x height:y width:x 的书架,书架有一个挡板,可以放在高度为整数的位置 ( 1 , 2 , … , y − 1 ) (1, 2, \dots , y - 1) (1,2,…,y−1) ,请问把挡板放在哪里可以让所有的书本都放进书架里?
思路
比赛的时候因为没有“能放书架就一定放”所以一直WA,后来看讨论好像不是我们的问题而是忘记放spj了,那这个特例就先不考虑。
思路是找到最高的书放在下面一层,层板就根据书的高度放,上面一层放尽可能多的书,因为所有没法放在上面(可能较矮)一层的书本都只能放在下面,由此上层放的书越多,下面的空间越大。至于上面最多能放多少书,其实是一个01背包问题(宽度就是容量)。
代码
因为题目被删了所以拿队友的代码糊弄一下吧(
#include<bits/stdc++.h>
using namespace std;
struct node{
int w, h;
}a[10005];
typedef long long ll;
inline int read()
{
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
if(flag) return X;
return ~(X-1);
}
int dp[10005];
int main(){
int n, w, h;
ll sum = 0;
int maxh1 = 0;
n = read(); w = read(); h = read();
for(int i = 1 ; i <= n ; ++i){
a[i].w = read();
a[i].h = read();
if(a[i].w > w)maxh1 = h + 1;
sum += a[i].w;
maxh1 = max(maxh1, a[i].h);
}
if(maxh1 > h || sum > w * 2){
cout << "impossible\n";
return 0;
}
if(sum <= w && maxh1 == h){
cout << "-1\n";
return 0;
}
dp[0] = -1;
for(int i = 1 ; i <= n ; i++){
for(int j = w ; j >= a[i].w ; --j){
if(dp[j - a[i].w]){
if(dp[j]){
dp[j] = min(dp[j], max(dp[j - a[i].w], a[i].h));
}
else{
dp[j] = max(a[i].h, dp[j - a[i].w]);
}
}
}
}
int maxh2 = 0x3f3f3f3f;
for(int i = sum - w; i <= w ; ++i){
if(dp[i])maxh2 = min(maxh2, dp[i]);
}
if(maxh2 == 0x3f3f3f3f || maxh1 + maxh2 > h){
cout << "impossible\n";
}
else{
cout << maxh1 << "\n";
}
}
Problem F: Flip Flow
题意
翻一个沙漏n次,给你每次翻的时间,求最后需要等多久所有沙子才会都回到下面一侧。
思路
签到题。题意我解释的不是很清楚建议直接看英文(虽然原题也写的很不清楚(确信
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int a[N];
int main()
{
int t, s, n;
cin >> t >> s >> n;
int low = 0;
a[n] = t;
for(int i = 0; i <= n; i++)
{
if(i < n)
cin >> a[i];
if(i == 0)
continue;
low = s - low - a[i] + a[i - 1];
if(low < 0)
low = 0;
if(low > s)
low = s;
}
cout << low << endl;
return 0;
}
Problem L:Lexicographical Lecturing
题意
给定互不相同且字典序升序排序的字符串组,找到区间 [ i , j ] [i, j] [i,j] ,使得所有字符串的在区间 [ i , j ] [i, j] [i,j] 上的这个子串互不相同,且字典序不变。
思路
若相邻两个串满足 S k , S k + 1 S_k, S_{k + 1} Sk,Sk+1 在 [ i , j ] [i, j] [i,j] 上字典序正确且相互独立,那么对于任何其他的串,在 [ i , j ] [i, j] [i,j] 上也一定满足这点。(仔细想想,其实是因为相邻两个串是最相近的,所以判断相邻串其实就是最优解)
由此,问题转化为找能使两个串之间独立的 [ i , j ] [i, j] [i,j] 的最大值,再从这些最大值中找最小值。至于如何找两个串之间的 [ i , j ] [i, j] [i,j] ,用背包找从 i i i 出发至少需要多少长度才能互不相同,即 d p [ i ] = a i dp[i] = a_i dp[i]=ai , [ i , i + a i ] [i, i + a_i] [i,i+ai] 范围内 i , j i, j i,j 独立。递推式如下(从字符串尾推到头):
if (x[i] < y[i]) dp[k][i] = 1;
else if (x[i] == y[i] && dp[k][i + 1] != INF) dp[k][i] = dp[k][i + 1] + 1;
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 505;
const int M = 20005;
vector<string> str;
int dp[N][M];
int dpp[M];
string tmp;
int main()
{
memset(dp, INF, sizeof(dp));
memset(dpp, -1, sizeof(dpp));
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++)
{
cin >> tmp;
str.push_back(tmp);
}
for (int k = 0; k < n - 1; k++)
{
string &x = str[k];
string &y = str[k + 1];
for (int i = m - 1; i >= 0; i--)
{
if (x[i] < y[i])
{
dp[k][i] = 1;
}
else if (x[i] == y[i] && dp[k][i + 1] != INF)
{
dp[k][i] = dp[k][i + 1] + 1;
}
}
}
int pos = -1;
for (int i = 0; i < m; i++)
{
for (int k = 0; k < n - 1; k++)
dpp[i] = max(dpp[i], dp[k][i]);
if (i == 0 || dpp[i] <= dpp[pos])
pos = i;
}
cout << pos + 1 << " " << pos + dpp[pos] << endl;
}
Problem M: Mixtape Management
题意
给出一个排列 p 1 , p 2 , … , p n p_1, p_2, \dots , p_n p1,p2,…,pn ,要你给出 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,…,an ,使得这个数组字典序上升序排列,且有 a p 1 < a p 2 < … < a p n a_{p_1} < a_{p_2}< \dots < a_{p_n} ap1<ap2<…<apn 。
思路
第 i i i 个数的构成为:前 i i i 个为 2 2 2 ,之后 n − i n - i n−i 个是 1 1 1 ,这部分保证了字典序;之后 p i p_i pi 个是 3 3 3 ,保证了大小。好巧妙的构造(
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e4 + 10;
string str;
int a[N];
queue<string> ans;
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
cin >> a[i];
str = "";
str.append(i + 1, '2');
str.append(n - i - 1, '1');
str.append(a[i], '3');
ans.push(str);
}
while(!ans.empty())
{
cout << ans.front() << ' ';
ans.pop();
}
return 0;
}
总结
五一太摸了现在才补题,忘记了,大概记得B题一直卡,但是可以算是主办方的锅(?