题解|2024暑期杭电多校02

【原文链接】

比赛题单:2024“钉耙编程”中国大学生算法设计超级联赛(2)

1001.鸡爪

构造

题目大意

一个鸡爪由一个中心结点和3条(不含端点的)边构成。如下图中,三条边和结点 1 1 1 构成一个鸡爪,结点 2 , 3 , 4 2,3,4 2,3,4 不属于这个鸡爪。

请添加图片描述

给定正整数 n n n ,允许使用 n n n 条边和任意个结点。要求构造一张图,在包含尽可能最多的鸡爪的条件下,按顺序输出图的 n n n 条边时的字典序最小。

解题思路

不限制结点数的情况下,想要构造最多 n / 3 n/3 n/3(整除)个的鸡爪是很容易的,问题的关键是如何构造使得字典序最小。

先讨论 n n n 是3的倍数的情况。
由于一个中心点只能被一个鸡爪使用,贪心的想法便是让结点 1 1 1~ n / 3 n/3 n/3 成为中心点,且依次尽量让结点 1 , 2 , 3 1,2,3 1,2,3 连接更多的点。

4 4 4~ n / 3 n/3 n/3 这些点首先作为中心点和 1 , 2 , 3 1,2,3 1,2,3 相连。
1 , 2 , 3 1,2,3 1,2,3 三个点互相连接后,无法构成以 1 , 2 , 3 1,2,3 1,2,3 为中心的鸡爪,因此需要借助 n / 3 + 1 , n / 3 + 2 , n / 3 + 3 n/3+1, n/3+2, n/3+3 n/3+1,n/3+2,n/3+3 这三个点进行构造。

连接图如下:
请添加图片描述
其他情况:
对于 n n n 不是3的倍数的情况,将多余的1或2个点连接到结点1上。
n ≤ 6 n\le 6 n6时,由于结点数太少,需要单独构造。

参考程序

vector<vector<pll>> ans={
    {}, // 0
    {{1,2}},
    {{1,2},{1,3}},
    {{1,2},{1,3},{1,4}}, // 1
    {{1,2},{1,3},{1,4},{1,5}},
    {{1,2},{1,3},{1,4},{1,5},{1,6}},
    {{1,2},{1,3},{1,4},{1,5},{2,3},{2,4}}, // 2
};
void solve()
{
    ll n;cin >> n;
    if(n<=6){
        for(auto p:ans[n]){
            cout << p.first << ' ' << p.second << endl;
        }return ;
    }
    vector<pll> edges;
    ll cnt = n/3;
    FORLL(i,2,cnt+3) edges.emplace_back(1,i); 
    FORLL(i,3,cnt+2) edges.emplace_back(2,i);
    FORLL(i,4,cnt+1) edges.emplace_back(3,i); 
    FORLL(i,1,n%3) edges.emplace_back(1,cnt+3+i);
    SORT(edges);
    for(auto p:edges){
        cout << p.first << ' ' << p.second << endl;
    }
    // cout << "Test:" << edges.size() << endl;
}

1003.绝对不模拟的简单魔方

题目大意

给定一个三阶魔方,用 1 1 1~ 6 6 6表示6个面的颜色。
魔方从初始还原状态起,侧面被扭转了不超过3次,且有一个角上的两片颜色贴纸可能贴错。
确定魔方的贴纸是否被贴错。如果有贴错,输出贴错那个角的三个颜色。

解题思路

扭转三次的操作对于魔方的角来说没有任何影响,只是虚晃一枪(题目中也提示了“绝对不模拟”)。

但是赛时还是有神速度写模拟而且一遍过了%%%%

只需要先把正常状态的魔方的8个角按同一顺序(如顺时针)记录下来,统一以 1 , 6 1,6 1,6开头以唯一确定三元组。
再把输入的魔方的8个角按同一顺序(顺时针)记录下来,统一以 1 , 6 1,6 1,6开头,然后在正常状态的记录中查找,找不到的就是被贴错贴纸的角。

参考程序

struct cor{
    int a,b,c;
    cor(int _a,int _b,int _c):a(_a),b(_b),c(_c){}
    bool operator==(cor t)const{
        return a==t.a&&b==t.b&&c==t.c;
    }
    void srt(){ //按大小排序
        if(a>b) swap(a,b);
        if(b>c) swap(b,c);
        if(a>b) swap(a,b);
    }
    void mkstd(){ //把三元组化为以1或6开头的形式
        int t;
        while(a!=1&&a!=6){
            t=a; a=b; b=c; c=t;
        }
    }
};
//原始魔方八个角,顺时针
vector<cor> std_cor = {{1,2,3},{1,3,4},{1,4,5},{1,5,2},{6,3,2},{6,4,3},{6,5,4},{6,2,5}};
void solve()
{
    vector<string> mf(9);
    for(auto& s:mf) cin >> s;
    vector<cor> corners={//给定魔方的八个角,顺时针
        {mf[0][3]-'0',mf[3][11]-'0',mf[3][0]-'0'},
        {mf[8][3]-'0',mf[5][0]-'0',mf[5][11]-'0'},
        {mf[3][8]-'0',mf[3][9]-'0',mf[0][5]-'0'},
        {mf[5][8]-'0',mf[8][5]-'0',mf[5][9]-'0'},
        {mf[6][3]-'0',mf[5][3]-'0',mf[5][2]-'0'},
        {mf[3][3]-'0',mf[2][3]-'0',mf[3][2]-'0'},
        {mf[6][5]-'0',mf[5][6]-'0',mf[5][5]-'0'},
        {mf[3][6]-'0',mf[2][5]-'0',mf[3][5]-'0'}};
    for(auto& cor:corners) cor.mkstd();
    for(auto& cor:corners){
        int fl=0;
        for(auto& st:std_cor) if(st==cor) {fl=1;break;}
        if(fl) continue;
        cor.srt();
        cout << cor.a << ' ' << cor.b << ' ' << cor.c << endl;
        return;
    }
    cout << "No problem" << endl;
}

1006.传奇勇士小凯

题目大意

一棵以 1 1 1 为根, n n n 个结点的树,在第 i i i 个结点处,每天有 p i 15 \dfrac{p_i}{15} 15pi 的概率可以离开当前结点,并选择一个子节点走一步。
从根结点 1 1 1 走到任意叶子结点的最大天数的期望。

解题思路

在第 i i i 个节点处,记停留在该结点的期望天数为 x x x ,有 p i 15 \dfrac{p_i}{15} 15pi 的概率可以离开(停留 1 1 1 天),有 1 − p i 15 1-\dfrac{p_i}{15} 115pi 的概率停留在当前结点(停留 x + 1 x+1 x+1 天)。
x = p i 15 + 15 − p i 15 ⋅ ( 1 + x ) x = \dfrac{p_i}{15} + \dfrac{15-p_i}{15} \cdot (1+x) x=15pi+1515pi(1+x) 成立,解得 x = 15 p i x = \dfrac{15}{p_i} x=pi15

问题转换为选择一条以结点 1 1 1 开头,以叶子结点结尾的路径,使得路径上的每个结点的期望天数 15 p i \dfrac{15}{p_i} pi15 之和最大。

参考程序

void form(pll& p){ //化简
    ll g = __gcd(p.first,p.second);
    p.first/=g;
    p.second/=g;
}
inline bool smaller(pll a,pll b){ //a<b
    return 1.*a.first/a.second<1.*b.first/b.second;
}
void addto(pll& a,pll b){ //a+=b
    a.first=a.first*b.second+a.second*b.first;
    a.second*=b.second;
    form(a);
}
vector<ll> vdfs; //dfs序列
vector<vector<ll>> G;
vector<ll> fa;
vector<pll> ans;
void dfs(ll x,ll f){
    fa[x]=f;
    vdfs.emplace_back(x);
    for(auto &y:G[x]) if(y!=f) dfs(y,x);
}
void solve()
{
    ll n;cin >> n;
    G.clear(); G.resize(n+1);
    fa.clear(); fa.resize(n+1);
    ans.clear(); ans.resize(n+1,{0,1});
    vdfs.clear();
    ll u,v,t;
    FORLL(i,1,n-1){
        cin >> u >> v;
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    } dfs(1,0);
    FORLL(i,1,n){
        cin >> t;
        ans[i]={15,t}; //直接存入第i个结点的期望
    }
    reverse(ALL(vdfs));
    for(auto cur:vdfs){ //逆DFS序遍历
        pll mx={0,1};
        for(auto &y:G[cur]) if(y!=fa[cur]){
            if(smaller(mx,ans[y])) mx=ans[y];
        }
        addto(ans[cur],mx);
    }
    cout << ans[1].first << '/' << ans[1].second << endl;
}

1007.URL匹配

题目大意

给定字符串URL,形如:s3://hdu-oj-bucket/problem=1/type=data/
从中提取出:

  • 传输协议:://之前的内容,如s3
  • 网络位置:://之后,第一个/之前的内容,如hdu-oj-bucket
  • 环境变量:由/分割,形如A=B的内容,如problem=1type=data

解题思路

按题目要求提取即可

参考程序

void solve()
{
    string s; cin >> s;
    string protocol;
    string location;
    vector<string> var;
    string varname,val;
    ll n=s.length(),i=0,t;
    for(i=0;i<n;i++){
        if(s[i]==':'){
            protocol = s.substr(0,i);
            break;
        }
        t = 1;
    } i+=3;
    ll j=i;
    for(;i<n;i++){
        if(s[i]=='/'){
            location = s.substr(j,i-j);
            break;
        }
    } i++;
    while(i<n){ //匹配varname=val,以/分隔
        j=i;
        while(j<n&&s[j]!='='&&s[j]!='/') j++;
        if(s[j]=='/'){ //跳过
            i=j+1;
            continue;
        }else if(s[j]=='='){
            varname=s.substr(i,j-i);
            i=j+1; j=i;
            while(j<n&&s[j]!='/') j++;
            val=s.substr(i,j-i);
            var.emplace_back(varname+"="+val);
        }
    }
    cout << protocol << endl;
    cout << location << endl;
    for(auto &x:var) cout << x << endl;
}

写完python才发现hduOJ不支持python…

import re

T = int(input())

for _ in range(T):
    url = input()
    protocol, location, *path = re.split(r"://|/", url)
    variables = [p for p in path if "=" in p]
    print(protocol)
    print(location)
    for variable in variables:
        print(variable)

1010.女神的睿智

题目大意

给定长度为 8 8 8的字符串,由R,G,B三种字符组成。
进行以下三轮操作,每轮操作从左往右两两合并:

  1. 颜色相同时,合并为该颜色;颜色不同时,合并为左侧颜色
  2. 颜色相同时,合并为该颜色;颜色不同时,合并为左侧颜色
  3. 颜色相同时,合并为该颜色;颜色不同时,比较两种颜色在初始字符串中的数量,合并为数量多的一种;数量相同时,合并为随机颜色

求最终合并后的颜色:R,G,B或不确定颜色N

解题思路

按题目要求模拟即可

参考程序

void solve()
{
    string s,s2; cin >> s;
    map<char,int> mp;
    for(auto c:s) mp[c]++;
    for(ll i=0;i<8;i+=2) s2.push_back(s[i]);
    s.clear();
    for(ll i=0;i<4;i+=2) s.push_back(s2[i]);
    if(s[0]==s[1]) cout << s[0] << endl;
    else if(mp[s[0]]!=mp[s[1]]) cout << s[mp[s[0]]<mp[s[1]]] << endl;
    else cout << 'N' << endl;
}

1011.在 A 里面找有 C 的 B

//TODO

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深翼CCLMSY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值