睿抗2023-03-05

今天给大家分享的是2023年睿抗开发大赛中的第三题以及第五题

因为这是正经的算法真题,所以难度还是有的,题目也比较综合,希望大家可以好好学习这部分。

3.

RC-u3 骰子游戏

分数 20

在某个游戏中有一个骰子游戏。在游戏中,你需要投掷 5 个标准六面骰子(骰子为一个正方体,6 个面上分别有1、2、3、4、5、6中的一个数字,骰子的质量均匀),投出的点数根据组合会获得一个“获胜等级”。获胜等级从高到低如下:

五个同点数 - 五个骰子显示相同的点数
四个同点数 - 四个骰子显示相同的点数
葫芦 - 一对和一个三个同点数(如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)
无 - 除去以上的其他情况
给定你已经投出的一次结果,现在假设你可以选择任意个骰子重投一次,请问怎么样操作,才能最大化在重骰后获得更好的获胜等级的概率呢?

注意:更好的获胜等级需要严格地比当前的获胜等级更好,例如 1、1、2、2、3 如果重骰后变为 1、1、3、3、4 并不比当前的获胜等级更好。

输入格式:
输入第一行是一个正整数 T (1≤T≤10),表示接下来有多少组数据。
每组数据只有一行 5 个数字,表示第一次投出的 5 个骰子的点数。

输出格式:
对于每组数据输出三个整数,其中第一个整数为为了获得最大的概率需要重新骰几个骰子,后面的两个整数为重骰骰子后概率的最简分数,其中第二个整数为分子,第三个整数为分母。如果分子为 0,分母为 1。

如果有多种获得最大概率的情况,取重骰的骰子数最少的方案。

代码部分

#include<iostream>
#include<map>
using namespace std;
int arr[6],b[6];
int n=5;
int p,ct;
vector<int>v;
map<int,int>mp;
struct info{
    int x;
    int y;
    int cnt;
};
info ans;
int gcd(int a,int b){
    if(b==0)return a;
    return gcd(b,a%b);
}
int lcm(int a,int b){
    return a/gcd(a,b)*b;
}
int check(int a[]){
    mp.clear();
    for(int i=1;i<=n;i++){
        mp[a[i]]++;
    }
    if((int)mp.size()==1)return 1;
    if((int)mp.size()==2){
        for(auto&[x,y]:mp){
            if(y==4){
                return 2;
            }
            if(y==3){
                return 3;
            }
        }
    }
    if((int)mp.size()==5&&!mp.count(1))return 4;
    if((int)mp.size()==5&&!mp.count(6))return 5;
    if(mp.size()==3){
        for(auto&[x,y]:mp){
            if(y==3)return 6;
            if(y==2)return 7;
        }
    }
    if(mp.size()==4)return 8;
    return 9;
}
void dfs(int now){
    if(now==v.size()){
        if(check(b)<p)++ct;
        return;
    }
    for(int i=1;i<=6;i++){
        b[v[now]]=i;
        dfs(now+1);
    }
}
void solve(){
for(int i=1;i<=n;i++)cin>>arr[i];
ans.x=0;
ans.y=0;
ans.cnt=0;
p=check(arr);
if(p==1){
    cout<<0<<" "<<0<<" "<<1<<'\n';
    return;
}
int q=1;
for(int k=1;k<=5;k++){
    q*=6;
    ans.x*=6,ans.y*=6;
    for(int i=0;i<1<<n;i++){
        if(__builtin_popcount(i)!=k)continue;
         v.clear();
         ct=0;
         for(int j=1;j<=5;j++)b[j]=arr[j];
         for(int j=0;j<5;j++){
            if(i>>j&1){
                v.push_back(j+1);
            }
         }
         dfs(0);
         if(ans.y==0){
           ans.x=ct;
           ans.y=q;
           ans.cnt=k;
         }else{
            if(ct>ans.x){
                ans.x=ct;
                ans.y=q;
                ans.cnt=k;
            }
         }

    }
}
int t=gcd(ans.x,ans.y);
cout<<ans.cnt<<" "<<ans.x/t<<" "<<ans.y/t<<'\n';
}
int main(){
 int t;
 cin>>t;
 while(t--){
    solve();
 }
 return 0;
}

解决这道题首先还是要理解这道题:

1.再选择调整个数的时候,巧妙的和状态压缩DP结合,这是大家可以学习的点

2.同时对于每一次循环遍历要注意将原先的容器给清空,这里还要注意gcd和lcm可以记住这个模版,在以后也会经常用到。

第五题:

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

分数 30

注意:题面内容表达纯属娱乐,与现实无关!

网上常有人说:看 XX 只能度过一个相对成功/失败的人生。不妨假设把这个句式套用在“参加睿抗比赛“以及“玩手机游戏”上,那么有:

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

输入格式:
输出第一行为一个正整数 T (1≤T≤5),表示数据组数。

每组数据第一行是一个正整数 N (1≤N≤105),表示总共的人数。

接下来的 N 行,第 i 行有两个数字 Ai​,Bi​,表示第 i 位参赛选手是否参加了睿抗比赛以及是否玩手机游戏,0 为没有参加/没有玩,1 为参加了/玩了。

最后一行有 N 个数,为一个选手编号 1 到 N 的排列,表示选手成功程度的排序。排序顺序从最成功到最失败。

选手编号从 1 开始。

代码部分:

#include<iostream>
#include<map>
using namespace std;
int b[100],q[100],n;
struct info{
    int x;
    int y;
    int id;
}arr[100];
bool compare(const info&a,const info& b){
    if(a.x!=b.x)return a.x>b.x;
    return a.y<b.y;
}
int get(){
    int rr=0;
    for(int i=1;i<=n;i++){
    if(rr==0||b[i]>=q[rr]){
        q[++rr]=b[i];
        continue;
    }
    int l=1,r=rr;
    while(r>l){
        int mid=l+r>>1;
        if(q[mid]<=b[i])l=mid+1;
        else r=mid;
    }
    q[l]=b[i];
    }return rr;
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>arr[i].x>>arr[i].y,arr[i].id=i;
sort(arr+1,arr+1+n,compare);
for(int i=1;i<=n;i++)cin>>b[i];
int cnt=0;
arr[0].x=-1;
map<int,int>mp;
for(int i=1;i<=n;i++){
    if(arr[i].x==arr[i-1].x&&arr[i].y==arr[i-1].y){
        mp[arr[i].id]=mp[arr[i-1].id];
    }else if(arr[i].x==arr[i].y&&arr[i].x!=arr[i-1].x&&arr[i-1].x==arr[i-1].y){
        mp[arr[i].id]=mp[arr[i-1].id];
    }else{
        mp[arr[i].id]=++cnt;
    }
}
for(int i=1;i<=n;i++){
b[i]=mp[b[i]];
}
cout<<n-get()<<'\n';
}
int main(){
int t;
cin>>t;
while(t--){
    solve();
}
return 0;
}

1.首先这道题要注意一个转换的思想,要用输入的arr进行排序得出存在的序列,并用等级去记录,再是用实际的排列通过这个等级去排列,看有几个最大的上升子序列即可

2.这道题使用的求出最长上升子序列的方法可以当作模版,等号可以取是因为题目要求,相同等级的人可以放到一起,如果是严格的上升子序列记得把等号给去掉,求出的res个数是正确的但是数组里面的元素不是正确的,这一点要注意区分。

好了,今天的分享就到这里,希望大家多多关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值