Codeforces Round #782 (Div. 2) A-E题

本文通过A到E五个题目,展示了不同类型的算法问题及其解决方案,包括数组排列、位操作、最短路径、排序序列构造和图的连通性判断。通过对每道题目的思路分析,揭示了贪心策略、回溯法和并查集等算法在解决实际问题中的应用。
摘要由CSDN通过智能技术生成

A题-Red Versus Blue

在这里插入图片描述在这里插入图片描述

思路

  • 尽可能使得R平均在B的空隙中分配就好
    #include<iostream>
    #include<vector>
    #include<string>
    #include<set>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include <chrono>
    #include<math.h>
    #include<unordered_map>
    #include <bits/stdc++.h>
    using namespace std;
     
    int main()
    {
        int t;
        cin >> t;
        for(int o = 0;o<t;o++)
        {
            int n,r,b;
            cin >> n >> r  >> b;
            int each = r / (b+1);
            int add = r - r / (b+1) * (b+1);
            string s = "";
            int i;
            for( i = 0;i<add;i++)
            {
                for(int k = 0;k<each+1;k++) s+="R"; 
                s += "B";
            }
            for(;i<b+1;i++)
            {
                for(int k = 0;k<each;k++) s+="R";
                s+="B";
            }
            s = s.substr(0,s.size() - 1);
            cout << s << endl;
        }
        system("pause");
        return 0;
    }

B题(BIT FLIPPING)

在这里插入图片描述在这里插入图片描述

思路

  • 分k是奇数,k是偶数讨论。
  • 贪心:尽量使得高位为1
  • 剩余的次数累加到最后
    #include<iostream>
    #include<vector>
    #include<string>
    #include<set>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include <chrono>
    #include<math.h>
    #include<unordered_map>
    #include <bits/stdc++.h>
    using namespace std;
     
    int main()
    {
        int t;
        cin >> t;
        for(int o = 0;o<t;o++)
        {
            int n,k;
            cin >> n >> k;
            string s;
            int k1 = k;
            cin >> s;
            int ans[n];
            memset(ans,0,n*sizeof(int));
            int pre_filp = 0;
            for(int i = 0;i<s.size();i++)
            {
                if(k==0) break;
                char c;
                if(pre_filp %2 ==  0) c = s[i];
                else if(s[i] == '1') c = '0';
                else if(s[i] == '0') c = '1';
                if(c == '0')
                {
                    if(k%2==0) {pre_filp++;ans[i]++;k--;}
                }
                if(c == '1')
                {
                    if(k%2!=0) {pre_filp++;ans[i]++;k--;}
                }
            }
            ans[n-1]+=k;
            string a;
            for(int i = 0;i<n;i++)
            {
                if(((k1 - ans[i])) % 2 ==0) a += s[i];
                else
                {
                    if(s[i] == '0') a+="1";
                    else a += "0";
                }
            }
            cout << a << endl;
            for(int i = 0;i<n;i++)
            {
                cout << ans[i];
                if(i < n - 1) cout << " ";
            }
            cout << endl;
        }
        system("pause");
        return 0;
    }

C题(Line Empire)

在这里插入图片描述在这里插入图片描述

思路

  • 枚举首都最后的位置,然后贪心计算。
    #include<iostream>
    #include<vector>
    #include<string>
    #include<set>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include <chrono>
    #include<math.h>
    #include<unordered_map>
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int main()
    {
        int t;
        cin >> t;
        for(int i = 0;i<t;i++)
        {
            ll n,a,b;
            cin >> n >> a >> b;
            ll x[n+1];
            x[0] = 0;
            for(int k = 0;k<n;k++)
            {
                cin >> x[k+1];
            }
            ll pre[n+1];
            pre[n] = 0;
            for(int k = n-1;k>=0;k--)
            {
                pre[k] = pre[k+1] + (n - k) * (x[k+1] - x[k]);
            }
            ll ans = LONG_LONG_MAX;
            for(int k = 0;k<n+1;k++)
            {
                ans = min(ans,pre[k] * b + (a+b) * (x[k] - x[0]));
            }
            cout << ans << endl;
        }
        system("pause");
        return 0;
    }

D题(Reverse Sort Sum)合理构建子问题GOOD

在这里插入图片描述
在这里插入图片描述

思路(从后往前更简单)

  • 从前往后(复杂):分类讨论,从第一个数开始考虑。考察后面多加多少个1才能使得和等于目标值。思路较模糊
    #include <iostream>
    #include <vector>
    #include <string>
    #include <set>
    #include <algorithm>
    #include <map>
    #include <queue>
    #include <chrono>
    #include <math.h>
    #include <unordered_map>
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int main()
    {
        int t;
        cin >> t;
        for (int i = 0; i < t; i++)
        {
            int n;
            cin >> n;
            int c[n];
            for (int k = 0; k < n; k++)
                cin >> c[k];
            vector<int> ans;
            int k = 0;
            int pre_0 = 0;
            while (k < n && c[k] == 0)
            {
                ans.push_back(0);
                pre_0++;
                k++;
            }
            if (k < n)
                ans.push_back(1);
            //后面的每个和都不可能为0
            if (k < n)
            {
                int q = c[k] - k - 1;
                int p = 0;
                while (p < q)
                {
                    ans.push_back(1);
                    p++;
                }
                ans.push_back(0);
                p = k + 1;
                q = ans.size() - 1;
                while (p < n && q < n && ans.size() < n)
                {
                    int remain = c[p] - ans[p] * p - 1;
                    int l = 0;
                    int m = q - p;
                    while (l < remain - m)
                    {
                        ans.push_back(1);
                        q++;
                        l++;
                    }
                    if (ans.size() < n)
                    {
                        ans.push_back(0);
                        q++;
                    }
                    p++;
                }
            }
            k = 0;
            while (k < n - 1)
            {
                cout << ans[k] << " ";
                k++;
            }
            cout << ans[n - 1] << endl;
        }
        system("pause");
        return 0;
    }

当时考虑过从后往前做,但没有继续。

  • 从后往前考虑更简单。先确定最后一个是0还是1,根据最后一步排序格局**确定前n-1个数构成的sum集合,在执行完 f ( A , 1 ) , f ( A , 2 ) ⋯ f ( A , n − 1 ) f(A,1),f(A,2) \cdots f(A,n-1) f(A,1),f(A,2)f(A,n1)的前提下。使用和上一道progression(也是D题)相同的思路即可。
  • 再考虑前n-1个数构成的子问题。
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;




int main()
{
    int t;
    cin >> t;
    for(int i = 0;i<t;i++)
    {
        int n;
        cin >> n;
        int c[n];
        vector<int> ans;
        ll sum  = 0;
        for(int k  = 0;k<n;k++) {cin >> c[k];sum+=(ll)c[k];}
        int one = (int)((sum) / n);
        int predec = 0;
        int f[n];
        memset(f,0,n*sizeof(int));
        while(n>1)
        {
            if(c[n-1] - predec <=1) {ans.push_back(0);} else {ans.push_back(1);}
            if(one - 1 >= 1)
            {
                 predec++;
                if(n - one  >= 0) f[n-one]++;
            }
            one-=ans[ans.size() - 1];
            predec-=f[n-1];
            n--;
        }
        if(c[0] - predec >=1) ans.push_back(1); else ans.push_back(0);
        one -= ans[ans.size() - 1];
        for(int k = ans.size() - 1;k>=0;k--) 
        {
            cout << ans[k];
            if(k >  0) cout << " ";
        }
        cout << endl;
    }
    system("pause");
    return 0;
}

总结

  • 本质上还是对问题理解不够深入。选择从后往前是因为可以快速确定最后一步的格局,充分利用sum数组的总和等于原始数据中1的个数乘以n
  • 而从前往后没有这个性质。

E. AND-MEX Walk

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

思路

  • 解空间的缩小:显然 M E X < = 2 MEX<=2 MEX<=2
  • 讨论每种情况:MEX=0,MEX=1,MEX=2;
    • 若MEX=0,路径上的数需要至少一位bit恒保持1.
    • 若MEX=1,路径上前面几个数为至少从低位开始第二个bit为1的数,然后突变为0;
  • 对固定bit为1对应的所有边,使用并查集维护这些边构成的连通分量。
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int uf[30][100000];  //记录并查集信息
int h[30][100000];
int w[30][100000];   
int o[30][100000];
int find_set(int x,int s[])
{
    if(x != s[x]) s[x]=find_set(s[x],s);
    return s[x];
}

void union_set(int x,int y,int s[],int h[],int w[])
{
    x = find_set(x,s);
    y = find_set(y,s);
    if(x == y) return;
    if(h[x] == h[y])
    {
        h[x] = h[y] + 1;
        s[y] = x;
        w[x] = (w[y] & w[x]);
    }
    else
    {
        if(h[x] < h[y]) {s[x] = y;w[y] = (w[y] & w[x]);}
        else {s[y] = x;w[x] = w[y] & w[x];}
    }
}



int main()
{
    int n,m;
    cin >> n >> m;
    vector<pair<int,int>> adj[n]; //每个顶点的邻接表
    vector<array<int,3>> edge;
    for(int i = 0;i<m;i++)
    {
        int a,b,w;
        cin >> a >> b >> w;
        a--; b--; 
        adj[a].push_back(make_pair(b,w));
        adj[b].push_back(make_pair(a,w));
        edge.push_back({a,b,w});
    }
    //map<int,int> o[30];
    for(int u  =  0 ; u<30;u++)
    {
        //memset(w[u],- 1,n*sizeof(int));
        for(int i = 0;i<n;i++)  //初始化每个结点
        {
            w[u][i] = (1 << 30) - 1;
           // cout << w[u][i] << endl;
            h[u][i] = 0; 
            uf[u][i] = i;  //每个结点父亲是其自身
        } 
        for(array<int,3> a:edge)
        {
            if((a[2] >> u) & 1) 
            {
                union_set(a[0],a[1],uf[u],h[u],w[u]);
                w[u][find_set(a[0],uf[u])] &= a[2];
            }
        }
        for(int i = 0;i<n;i++)   //对于每个结点遍历邻接表
        {   
            int x = find_set(i,uf[u]);  //找所属集合
            for(pair<int,int> b : adj[i])
            {
                if(u > 0 && (w[u][x] & b.second) == 0)   //与该条边相与结果为0
                    o[u][x]++;
            }
        }
    }
    int q; cin >> q;
    for(int i = 0;i<q;i++)
    {
        int u,v; 
        cin >> u >> v;
        u--;v--;
        int flag = 3;
        for(int k = 0;k<30;k++)
        {
            //是否可能为0
            int u_s = find_set(u,uf[k]);
            int v_s = find_set(v,uf[k]);
            if(u_s == v_s)
            {
                flag = min(flag,1);
                break;
            }
            else if((o[k][u_s] > 0 ))
            {
                flag = min(flag,2);
            } 
        }
        if(flag == 3)  cout << 2 << endl;
        else if(flag == 2) cout << 1 << endl;
        else cout << 0 << endl;
    }
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值