2021年度训练联盟热身训练赛第七场(GCPC2020)

本文详细介绍了2021年训练联盟热身训练赛第七场的五道算法题目,包括AdolescentArchitecture、BookshelfBuilding、FlipFlow、LexicographicalLecturing和MixtapeManagement的题意、解题思路及C++代码实现。题目涉及几何排序、01背包、动态规划和字符串处理等算法知识。
摘要由CSDN通过智能技术生成

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,,y1) ,请问把挡板放在哪里可以让所有的书本都放进书架里?

思路

比赛的时候因为没有“能放书架就一定放”所以一直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} ap1ap2apn

思路

i i i 个数的构成为:前 i i i 个为 2 2 2 ,之后 n − i n - i ni 个是 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题一直卡,但是可以算是主办方的锅(?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值