2023 睿抗机器人开发者大赛CAIP-编程技能赛-本科组(省赛)

2023 睿抗机器人开发者大赛CAIP-编程技能赛-本科组(省赛)

RC-u1 亚运奖牌榜

题意

给定两个国家/地区的金银铜牌的获得记录,输出他们的金银铜牌的具体记录,并输出哪个国家/地区排名高

思路

直接读入后比较即可

代码

#include
using namespace std;
int a[5],b[5];
void solve(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        int wh,op;
        cin>>wh>>op;
        if(wh==0)a[op]++;
        if(wh==1)b[op]++;
    }
    for(int i=1;i<=3;i++)cout<b[i]){
            cout<<"The first win!\n";
            return;
        }
        if(a[i]>T__;
    while(T__--)solve();
    return 0;
}

RC-u2 出院

题意

给定n个已知字符的等级,现在给你m个新的字符串,请给这些字符串定级

规则:如果字符串在之前已经定过级了,那么直接输出等级,否则就尝试将字符串拆分为两个已知的字符串,如果只有一种拆法,那么等级就为这两个字符串等级的拼接,否则该字符串的等级为D

思路

我们直接枚举所有切割位置判断合法的切割情况的种数即可,用substr可快速切割字符串

代码

#include
using namespace std;
// #define int long long
void solve(){
    int n,m;
    cin>>n>>m;
    mapmp;
    for(int i=1;i<=n;i++){
        string a;
        string b;
        cin>>a>>b;
        mp[a]=b;
    }
    for(int tt=1;tt<=m;tt++){
        string s;
        cin>>s;
        if(mp.count(s)){
            cout<>T__;
    while(T__--)solve();
    return 0;
}

RC-u3 骰子游戏

题意

现在有5个六面骰子,问最少需要在重新摇几颗筛子才能使得摇完后能变成更高的等级的概率最大

每种情况等级(从高到低):

  • 五个同点数 - 五个骰子显示相同的点数
  • 四个同点数 - 四个骰子显示相同的点数
  • 葫芦 - 一对和一个三个同点数(如1、1、3、3、3)
  • 六高顺子 - 投出的点数为 2、3、4、5、6
  • 五高顺子 - 投出的点数为 1、2、3、4、5
  • 三个同点数 - 三个骰子显示相同的点数(如1、1、1、2、3)
  • 两对 - 投出的点数中有两对是相同的(如 1、1、2、2、3)
  • 一对 - 投出的点数有一对是相同的(如 1、1、2、3、4)
  • 无 - 除去以上的其他情况

思路

直接手算答案很麻烦,也不能保证答案的正确率,那么我们应该怎么做呢?

题目看起来复杂,实际上我们只要对每种情况规定好等级以后就好做很多。

最后要求至少需要重新摇几颗骰子才能使得等级能变高的概率最大

Question:那我们怎么知道重新摇几颗呢?

Answer:枚举

Question:那我们怎么知道那几颗需要重新摇呢?

Answer:枚举

Question:那我们怎么知道每颗骰子重新摇后的点数呢?

Answer:枚举

由此可见,我们把问题拆分为若干个小问题后每个问题我们实际上都可以通过枚举解决

第一步,重新摇几颗,从0枚举到5即可

第二步,哪几颗重新摇,我们可以写出第一个DFS,每一次在未被选中的骰子里选一颗,直到选满第一步所确定的颗数

第三步,重摇后的点数,如果是重摇的骰子则是1~6,如果不用重摇,那么骰子的值就是原来的值,所以我们可以写出第二个DFS,一颗一颗骰子进行分析,直到5颗骰子都考虑完以后计算在当前情况下的等级,统计有多少种等级是比原先等级更优,最后我们求得的概率即为 比原来更有的情况数 6 重摇骰子数 \frac{比原来更有的情况数}{6^{重摇骰子数}} 6重摇骰子数比原来更有的情况数

至于最后的输出分数,我们只要把分子和分母分开考虑后最后输出约分后(÷gcd)的值即可

代码

#include
using namespace std;
int lev(vectora){
    vectorcnt(7,0);
    for(int i=0;i<5;i++)cnt[a[i]]++;
    int fla5=0,fla4=0,fla3=0,fla2=0;
    for(int i=1;i<=6;i++){
        if(cnt[i]==5)fla5=1;
        if(cnt[i]==4)fla4=1;
        if(cnt[i]==3)fla3=1;
        if(cnt[i]==2)fla2++;
    }
    if(fla5)return 9;
    if(fla4)return 8;
    if(fla2&&fla3)return 7;
    if(cnt[2]&&cnt[3]&&cnt[4]&&cnt[5]&&cnt[6])return 6;
    if(cnt[1]&&cnt[2]&&cnt[3]&&cnt[4]&&cnt[5])return 5;
    if(fla3)return 4;
    if(fla2==2)return 3;
    if(fla2==1)return 2;
    return 1;
}
int vis[10];//用于标记那几颗骰子改变
int a[10];
int ans[10];//用于标记重摇i颗骰子后,等级比原来高的情况总数
int llve;//初始给定的骰子等级
vectornow;//辅助遍历所有重摇情况
int cc;//情况数
void sol(int dep){//假设有goal颗骰子重新摇,此时vis[i]=1的位置是需要动的,vis[i]=0的位置不变
    if(dep==6){
        if(lev(now)>llve)cc++;//和原来的等级进行比较,需要比原来等级高
        return;
    }
    if(vis[dep]==0){//当前骰子是固定的
        now.push_back(a[dep]);
        sol(dep+1);
        now.pop_back();
    }
    else{//当前骰子重新摇
        for(int i=1;i<=6;i++){
            now.push_back(i);
            sol(dep+1);
            now.pop_back();
        }
    }
}
void dfs(int dep,int goal,int st){
    if(dep==goal){
        cc=0; 
        sol(1);
        ans[goal]=max(ans[goal],cc);
        return;
    }
    for(int i=st;i<=5;i++){
        if(vis[i])continue;
        vis[i]=1;
        dfs(dep+1,goal,i+1);
        vis[i]=0;
    }
}
void solve(){
    vectorp;
    for(int i=1;i<=5;i++)cin>>a[i],p.push_back(a[i]);
    llve=lev(p);
    for(int i=1;i<=5;i++)ans[i]=0;
    double ma=0;
    for(int tt=0;tt<=5;tt++)dfs(0,tt,1),ma=max(ma,(double)ans[tt]/pow(6,tt));//选tt颗修改
    for(int tt=0;tt<=5;tt++){
        if(ma!=(double)ans[tt]/pow(6,tt))continue;
        int res1=ans[tt];
        int res2=pow(6,tt);
        cout<>T__;
    while(T__--)solve();
    return 0;
}

RC-u4 相对论大师

题意

给定若干个推论,需要从这些推论中选出若干条推论组成一个首尾矛盾的推论,如果有多种情况,输出最短那一条。

思路

实际上就是找最小环的问题,问题的一部分难点在于如何读入数据,我们需要用一个节点代替一个结论的正面或者反面,推论的关系实际上就是点和点之间通过单向边连接的关系。我们可以直接对所有节点都当其是其实节点考虑一遍推论是否能推出矛盾,如果可以则记录下环的最小长度。最后在这些节点中选择最优解即可。

代码

#include
using namespace std;
// #define int long long
map,int>name;//[结论,正反面]是一个节点,拥有独立的编号
map>rname;//每个节点编号下的信息:(是什么结论,是正面还是反面)
int id;//结合name食用
vectoredge[10005];//建边
int minn=1e9;//记录闭环的最小长度
vectorres[10005];//以每个位置为起点下的最小闭环
vectorfa;
int vis[10005];
void dfs(int now/*当前起点编号*/,int i/*起点节点编号*/){
    if(rname[now].first==rname[i].first&&rname[now].second==1-rname[i].second){
        if((res[i].size()>fa.size())||res[i].size()==0)res[i]=fa;
        minn=min(minn,(int)res[i].size());
        return;
    }
    for(auto v:edge[now]){
        if(vis[v])continue;
        vis[v]=1;
        fa.push_back(v);
        dfs(v,i);
        fa.pop_back();
        vis[v]=0;
    }
}
void solve(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        string a,b;
        int op1,op2;
        cin>>a>>op1>>b>>op2;
        if(name.count({a,op1})==0)name[{a,op1}]=++id,rname[id]={a,op1};
        if(name.count({b,op2})==0)name[{b,op2}]=++id,rname[id]={b,op2};
        if(name[{a,op1}]==name[{b,op2}])continue;
        edge[name[{a,op1}]].push_back(name[{b,op2}]);
    }
    for(int i=1;i<=id;i++){
        vis[i]=1;
        fa.push_back(i);
        dfs(i,i);
        fa.pop_back();
        vis[i]=0;
    }
    //按条件输出即可
    for(int i=1;i<=id;i++){
        if(res[i].size()!=minn)continue;
        for(int j=0;j0&&j>T__;
    while(T__--)solve();
    return 0;
}

RC-u5 相对成功与相对失败

题意

参加比赛一定比不参加比赛成功,玩手机游戏一定比不玩手机游戏失败(仅为题意,不代表个人观点)

现在有N个人,已知这些人自己填写的是否参加了睿抗比赛以及是否玩手机游戏的情况,以及他们实际上的成功程度的排序顺序,请问最少有多少人在填写情况时说谎了?

思路

根据题意,参加比赛不玩手机>参加比赛玩手机=不参加比赛不玩手机>不参加比赛玩手机

我们将题目给定的01转化一下,1代表好的方面,0代表不好的方面
则有 11>10=01>00

进一步我们发现可以用0,1,2这三种数代表每个人的情况
a[i]=0:不参加比赛玩手机
a[i]=1:不参加比赛不玩手机 或者 参加比赛&玩手机
a[i]=2:参加比赛不玩手机
那么按照最终的排名从高到底排序后的数组a[]肯定是非递减的
但我们发现并不是所有人都符合要求的,那么如何求最小的撒谎人数呢
那我们肯定要尽可能的拟合更多的人
所以实际上就是求在一个序列中最长非递减子序列的长度
因此我们可以考虑dp
但同时我们发现实际上a[i]的值只有三种可能

d p [ i ] [ j ] dp[i][j] dp[i][j]表示截止当前位置,子序列的最大元素是j=0/1/2的最长非递增子序列长度

初始状态 d p [ 0 ] [ 0 ] = 0 ; dp[0][0]=0; dp[0][0]=0;

状态转移: d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , m a x d p [ i − 1 ] [ k ] + ( a [ i ] = = j ) ) ( j < = k < = 2 ) dp[i][j]=max(dp[i][j],max{dp[i-1][k]}+(a[i]==j))(j<=k<=2) dp[i][j]=max(dp[i][j],maxdp[i1][k]+(a[i]==j))(j<=k<=2)
最后求 n − max ⁡ ( d p [ n ] [ 0 ] , d p [ n ] [ 1 ] , d p [ n ] [ 2 ] ) 最后求n-\max{(dp[n][0],dp[n][1],dp[n][2])} 最后求nmax(dp[n][0],dp[n][1],dp[n][2])

代码

#include
using namespace std;
void solve(){
    int n;
    cin>>n;
    vector>b(n+1);
    for(int i=1;i<=n;i++){
        cin>>b[i].first>>b[i].second;
        b[i].second=1-b[i].second;
    }
    vectora(n+1,0);
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        a[i]=b[x].first+b[x].second;
    }
    vector>dp(n+1,vector(5,0));
    for(int i=1;i<=n;i++){
        dp[i][0]=max({dp[i-1][0],dp[i-1][1],dp[i-1][2]})+(a[i]==0);
        dp[i][1]=max({dp[i-1][1],dp[i-1][2]})+(a[i]==1);
        dp[i][2]=dp[i-1][2]+(a[i]==2);
    }
    cout<>T__;
    while(T__--)solve();
    return 0;
}

你可能感兴趣的:(算法,c语言,c++)