2021 RoboCom 世界机器人开发者大赛-本科组(复赛)

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021 RoboCom 世界机器人开发者大赛-本科组(复赛)相关的知识,希望对你有一定的参考价值。

文章目录

7-1 冒险者分队 20

7-1 冒险者分队
分数 20
作者 DAI, Longao
单位 杭州百腾教育科技有限公司
冒险者分队是人气 MMORPG《最终幻想 14》里的一个游戏系统。玩家通过招募 NPC (非玩家角色)组成小队完成特定任务后可以获取丰厚的奖励。

由于完成任务有能力的要求,因此我们需要对 NPC 进行一定的训练。NPC 组成的小队会有三个属性:体能、心智,以及战术,玩家可以选择以下的两种训练课程之一对小队进行训练:

提升其中一个属性 40,降低其他两个属性各 20;
提升其中两个属性 20,降低剩下一个属性 40。
如果在选择的训练课程后有任意一个属性小于 0,那么训练会失败,属性不会发生变化。

为了完成特定任务,现在给定小队的初始属性和目标属性,请回答是否有可能通过一定的训练,使得小队的属性正好达到目标属性的值,如果可以的话,最少的次数是多少?

输入格式:
输入第一行是一个正整数 T (≤10
5
),表示有多少组询问。

接下来的 T 组询问,每组询问有两行,每行三个非负整数,第一行为小队初始的属性,第二行为需要达成的目标属性。

所有属性值均大于等于 0,小于等于 2×10
9

输出格式:
如果目标属性无法通过训练达到,输出一行 −1,否则输出一个整数,表示达到目标属性的最少训练次数。

输入样例:
4
25 30 35
65 10 15
100 200 300
200 180 220
100 100 100
0 0 0
777 888 999
777 888 999
输出样例:
1
3
-1
0

  • 题意:给出初始数组和目标数组,长都为3。每次可以给两个数+20,一个-40,或者两个-20,一个加40,求最少多少次能变成目标数组。
  • 思路:
    • 首先,初始数组和目标数组之间只能通过20来变换,所以对应位置的值作差%20,有余数肯定直接-1。然后把三个差值/20,简化问题。
    • 然后,考虑非法的情况,不管是-1,-1,+ 2还是 -2,+1,+1,这三个数%3的值都是1,因为最后要让差值从(a,b,c)变成(0,0,0),所以原本的这三个差值必须%3相等,否则-1。
    • 最后,考虑构造最优解。 设a < b < 0 < c,我们以1,1,-2作用于三者,那么b会首先变成0。再然后,再以两个操作2,-1,-1和1,1,-2组合成3,0,-3作用于a和c,就可以构造出(0,0,0)。(如果a或c不能被3整除怎么办?事实是不会的,因为上面已经判过三者对3同余了,所以三者%3都等于0,必定是能整除的)
#include<bits/stdc++.h>
using namespace std;

int a[10], b[10], c[10];

int main()
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;  cin>>T;
    while(T--)
        for(int i = 1; i <= 3; i++)cin>>a[i];
        //-1
        int ok = 1;
        for(int i = 1; i <= 3; i++)
            cin>>b[i];  c[i] = b[i]-a[i];
            if(c[i]%20!=0)ok = 0;
            c[i] /= 20;
        
        if(a[1]+a[2]+a[3] != b[1]+b[2]+b[3])ok = 0;
        int mod = (c[1]%3+3)%3;
        for(int i = 2; i <= 3; i++)
            if((c[i]%3+3)%3 != mod)ok = 0;
        if(ok==0) cout<<"-1\\n";  continue; 
        //构造
        int num = 0;
        num += (c[1]>0)+(c[2]>0)+(c[3]>0);
        if(num==2)c[1]=-c[1], c[2]=-c[2], c[3]=-c[3];
        int x = min(c[1],c[2],c[3]), z = max(c[1],c[2],c[3]), y = c[1]+c[2]+c[3]-x-z;//排序
        int res = 0;
        res += -y;  x -= y;  z += y*2;  y = 0; //操作1
        res += z/3*2;  //操作2
        cout<<res<<"\\n";
    
    return 0;



7-2 拼题A打卡奖励 25

7-2 拼题A打卡奖励
分数 25
作者 陈越
单位 浙江大学
拼题 A 的教超搞打卡活动,指定了 N 张打卡卷,第 i 张打卡卷需要 m
i

分钟做完,完成后可获得 c
i

枚奖励的金币。活动规定每张打卡卷最多只能做一次,并且不允许提前交卷。活动总时长为 M 分钟。请你算出最多可以赢得多少枚金币?

输入格式:
输入首先在第一行中给出两个正整数 N(≤10
3
) 和 M(≤365×24×60),分别对应打卡卷的数量和以“分钟”为单位的活动总时长(不超过一年)。随后一行给出 N 张打卡卷要花费的时间 m
i

(≤600),最后一行给出 N 张打卡卷对应的奖励金币数量 c
i

(≤30)。上述均为正整数,一行内的数字以空格分隔。

输出格式:
在一行中输出最多可以赢得的金币数量。

输入样例:
5 110
70 10 20 50 60
28 1 6 18 22
输出样例:
40
样例解释:
选择最后两张卷子,可以在 50+60=110 分钟内获得 18+22=40 枚金币。

  • 题意:n个物品,有价值和体积,每个可以选一次,背包容量m,求最大价值。
  • 思路:
    • 01背包板子题???果然TLE(15分)。
      n < 1e3, m < 5e5,所以O(nm)1e8肯定会超时。
    • 发现金币数量ci < 30 成为一个突破口,此时所有物品总价值不超过1000∗30,因此可以转换原本的求 “最小容量(5e5)下的最大价值” 为 “最大价值(3e4)下的最小容量”,系数都是n,3e7勉强可以通过。
//15分
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int v[maxn], w[maxn];
int f[maxn*maxn];
int main()
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n, m;  cin>>n>>m;
    for(int i = 1; i <= n; i++)cin>>v[i];
    for(int i = 1; i <= n; i++)cin>>w[i];
    for(int i = 1; i <= n; i++)
        for(int j = m; j >= 0; j--)
            if(j>=v[i])f[j] = max(f[j], f[j-v[i]]+w[i]);
        
    
    cout<<f[m]<<"\\n";
    return 0;

//25分
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int v[maxn], w[maxn];
int f[maxn*maxn];
int main()
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n, m;  cin>>n>>m;
    int sum = 0;
    for(int i = 1; i <= n; i++)cin>>v[i];
    for(int i = 1; i <= n; i++)cin>>w[i], sum += w[i];
    memset(f,0x3f,sizeof(f));
    f[0] = 0;
    for(int i = 1; i <= n; i++)
        for(int j = sum; j >= w[i]; j--)
            f[j] = min(f[j], f[j-w[i]]+v[i]);
        
    
    for(int i = sum; i >= 0; i--)
        if(f[i]<=m)
            cout<<i;  return 0;
        
    
    return 0;



7-3 快递装箱 25

7-3 快递装箱
分数 25
作者 陈越
单位 浙江大学
bt.png

有一条快递装箱的流水线是这样设计的(见下图):

fig.png

快递件从 A 口进入流水线转盘;到达 B 时进行称重,如果重量大于 W
1

就开一个新的箱子把它装进去,否则直接让它通过;到达C时检查箱子的重量,如果超过了 W
2

(>W
1

),就直接装车 —— 这条线有 1 号摄像头拍摄装车的箱子编号并记录;重量不达标的箱子或快递件继续行进到 D,在这里进行装箱处理。这里分几种情况:

如果 D 当前是空的,那么新开一箱给到达的快递件,或者如果到达的是一只箱子,那么等待下一个快递件到达;
如果 D 当前不是空的,那么肯定是有一只箱子。这时考察下一个物体 —— 如果下一个是快递件并且能装入这个箱子(即总重量不超过箱子的最大容量 W
max

),则将其装入;如果下一个是快递件但装不下了(这时新的快递件装箱),或者下一个来的就是箱子,或者已经没有货物过来了,则停止当前的装箱工作,检查当前这个箱子的重量,超过 W
2

就装车 —— 这条线有 2 号摄像头拍摄装车的箱子编号并记录;如果重量不足,则继续前进到 A 口,与新到的快递件汇合。D 点继续处理下一个排队的箱子或快递件。
而到 A 口的箱子则要看汇合的快递件能否装入:如果可以就装箱,向 B 进发;不行就一直等待,直到下一个可以装箱的快递装进去,或者没有任何新的快递到达,才继续向 B 进发。当有多只箱子从 D 转过来时,按到达的顺序排队。

简单起见,我们假设快递件匀速进入流水线,所有快递件从一个点到下一个点都只需要一个单位时间,并且在 B 和 C 的停留时间可忽略不计。当 B 发现重量大于 W
1

的是已经在箱子里的货物时,则不必再新开一个箱子。

输入格式:
输入在第一行给出 4 个正整数:N 为快递件的数量;W
max

、W
1

和 W
2

,如题面所述。其中 N≤10
4
,W
1

<W
2

<W
max

≤10
3

随后一行给出 N 个不超过 W
max

的正整数,为顺序到达的快递件的重量。

输出格式:
在第一行中先后输出第 1、2 号摄像头拍摄的装车箱子的数量、以及最后转盘上剩下的箱子的数量。第二行按非递减序输出剩下的箱子的重量。如果没有箱子剩下,则输出 None。

同一行数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:
11 100 50 80
85 25 60 21 10 52 80 95 78 15 3
输出样例:
2 1 4
40 55 78 80
样例说明:
我们按照“单位时间:事件”的格式描述整个过程。

时间 A B C D
1 85 - - -
2 25 85装箱 - -
3 60 25 85装车,1号拍摄 -
4 21 60装箱 25 -
5 10 21 60一箱 25装箱
6 52 10 21 60一箱到,25一箱启动
7 80不能装箱,25一箱等待 52装箱 10 21装箱得到81一箱
8 95不能装箱,25一箱等待 80装箱 52一箱 10装箱得到91一箱
9 78不能装箱,25一箱等待 95装箱 80一箱 52一箱到,91一箱装车,2号拍摄
10 15装箱得到40一箱 78装箱 95一箱装车,1号拍摄 80一箱到,52一箱启动
11 3装箱得到55一箱 40一箱 78一箱 80一箱
此时摄像头 1 拍摄了 2 次,摄像头 2 拍摄了 1 次,转盘上还剩 4 只重量为 55、40、78 和 80 的箱子。

  • 题意:模拟流水线,塞个货物进传送带,然后往后运动,更新一下每个点的状态,如果有货物运出就答案加一,照着题目意思做就行。
  • 思路:
    A、D 点用双端队列处理(因为有塞回去的情况),B、C用普通队列处理。
    用一个 pair 存储货物状态,分别记录重量和是否装箱,每次按题目要求更新即可。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int maxn = 1010;

int n, wmax, w1, w2;
vector<int>ans;
int ans1, ans2;

deque<PII>A, D;
queue<PII>B, C;
void update()  //每次按DCBA的顺序处理一遍当前流水线上的箱子
    if(D.size()!=0)
        PII x = D.front();
        if(x.second == 0) //x没装箱,给x装箱
            D.pop_front();
            D.push_front(make_pair(x.first,1));
        else
            D.pop_front();
            if(D.size()==0)D.push_front(x);//x不是最后一个就纳入出队考核
            else
                PII y = D.front();
                if(y.second == 0)//y没装箱
                    if(y.first+x.first <= wmax)//给y+x装箱
                        D.pop_front();
                        D.push_front(make_pair(x.first+y.first, 1));
                    else
                        if(x.first>w2)ans2++; //x正式出队
                        else A.push_back(x);  //给x放回去A重生
                    
                else
                    if(x.first > w2)ans2++;  //x正式出队
                    else A.push_back(x);     //给x放回去A重生
                
                if(D.front().second == 0) //z没装箱,给z装箱
                    PII z = D.front();  D.pop_front();
                    D.push_front(make_pair(z.first,1));
                
            
        
    
    if(C.size()!=0)
        PII x = C.front();  C.pop();
        if(x.first > w2)ans1++;
        else D.push_back(x);
    
    if(B.size()!=0)
        PII x = B.front();  B.pop();
        if(x.first > w1)C.push(make_pair(x.first, 1));
        else C.push(x);
    
    if(A.size()!=0)
        B.push(A.front());  A.pop_front();
    


int main()
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>wmax>>w1>>w2;
    for(int i = 1; i <= n; i++)
        int x;  cin>>x;  //把x放到A口,考虑是否装箱
        if(A.size()==0)A.push_back(make_pair(x,0));
        else
            PII y = A.front();
            if(y.first + x <= wmax)
                A.pop_front();
                A.push_front(make_pair(y.first+x, 1));
            else
                A.push_front(make_pair(x,0));
            
        
        update(); //更新一下
    
    //solve
    for(int i = 1; i <= 10050; i++)update();//让流水线转很多轮
    queue<PII>D2; //临时队列
    while(D.size() != 0)
        PII x = D.front();  D.pop_front();
        if(x.first > w2)ans2++; else D2.push(x);
    
    while(D2.size()!=0) D.push_back(D2.front());  D2.pop(); 
    while(A.size()!=0)if(A.front().first != 0)ans.push_back(A.front().first); A.pop_front(); 
    while(B.size()!=0)if(B.front().first != 0)ans.push_back(B.front().first); B.pop(); 
    while(C.size()!=0)if(C.front().first != 0)ans.push_back(C.front().以上是关于2021 RoboCom 世界机器人开发者大赛-本科组(复赛)的主要内容,如果未能解决你的问题,请参考以下文章

2021 RoboCom 世界机器人开发者大赛-本科组(初赛)完结

2021 RoboCom 世界机器人开发者大赛-本科组(复赛)

2021 RoboCom 世界机器人开发者大赛-本科组(复赛)

2021 RoboCom 世界机器人开发者大赛-本科组(决赛)

2022 RoboCom 世界机器人开发者大赛-本科组(省赛)

2022 RoboCom 世界机器人开发者大赛-本科组(国赛)