• 2022”杭电杯“中国大学生算法设计超级联赛(1)3 4 8题解


    这一场中bitset用得比较多,比较考验位运算和状态压缩思想。

    1003-Backpack

    题目大意:
    将若干件物品装入背包,问是否存在一种方案将背包装满,且背包中的物品异或值最大。

    思路:
    01背包的变题,很容易就想到用dp[i][j][k]来表示前i件物品中取若干件,异或和为j,体积为k的方案是否存在。
    状态转移方程为dp[i][j][k] = dp[i-1][j][k] | dp[i-1][j^w][k-v],如果开三维数组的话,先不说时间复杂度能否接受,空间都不够用。
    由于只记录是否存在,因此考虑用二进制来进行状态压缩。于是用bitset中的第j位表示体积为j的方案是否存在。复杂度为常数比较大的O(n2)。
    再用滚动数组进行优化,只用开两个一维bitset数组即可。

    AC代码:

    #include 
    const int N = 1024;
    using namespace std;
    
    bitset<N> f[N], g[N]; // f[i][j]表示异或值为i体积为j的情况是否存在
    int n, m;
    void solve()
    {
        for (int i = 0; i < N; i++)
            f[i].reset(), g[i].reset();
        f[0][0] = 1; //异或和为0,体积为0的情况存在
        cin >> n >> m;
        int v, w;
        for (int i = 1; i <= n; i++)
        {
            cin >> v >> w;
            for (int j = 0; j < N; j++)
            {
                g[j] = f[j ^ w];
                g[j] <<= v;
            }
            for (int j = 0; j < N; j++)
                f[j] |= g[j];
        }
        for (int i = N - 1; i >= 0; i--)
        {
            if (f[i][m])
            {
                cout << i << endl;
                return;
            }
        }
        cout << "-1" << endl;
    }
    signed main()
    {
        ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
        int T;
        cin >> T;
        while (T--)
            solve();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    1004-Ball

    题目大意:
    给出n个点,求出所有三个点的组合,使得这三个点之间曼哈顿距离的中位数是素数。输出组合的个数。

    思路:
    最容易想到的方法是枚举两个点的组合,将这两个点的曼哈顿距离作为中位数,然后确定第三个点。但是这样的复杂度是O(n3),想要通过此题比较困难。
    考虑对这种方法进行优化:
    当枚举两个点时,如果能快速地获得符合的第三个点数,就可以复杂度降到O(n2)。
    首先将所有的边进行排序,然后从小到大进行枚举。
    因为边已经是有序的了,可以用bitset来维护曼哈顿距离小于当前边的点。
    f[i][j]来表示i和j的曼哈顿距离是否小于当前两点的曼哈顿距离,对于枚举的两点ab,只要将f[a]f[b]作异或运算,结果中1的个数就是符合的第三点数,因为只有0和1异或才会得到1,因此所有的1一定是大于当前的曼哈顿距离和小于当前的曼哈顿距离的异或。枚举完a和b后,更新f[a]f[b],令f[a][b]=1;f[b][a]=1; 复杂度为O(n3/64)可以看作是常数较大的O(n2)。

    AC代码:

    #include 
    const int M = 2e5 + 5;
    const int N = 2e3 + 5;
    using namespace std;
    int prime[M];
    bool isprime[M];
    int get_prime(int n)
    {
        int cnt = 0;
        for (int i = 1; i <= n; i++)
            isprime[i] = 1;
        isprime[1] = 0;
        for (int i = 2; i <= n; i++)
        {
            if (isprime[i]) prime[++cnt] = i;
            for (int j = 1; j <= cnt && i * prime[j] <= n; j++)
            {
                isprime[i * prime[j]] = 0;
                if (i % prime[j] == 0) break;
            }
        }
        return cnt;
    }
    struct edge
    {
        int x, y, w;
    } e[N * N];
    bool cmp(edge a, edge b) { return a.w < b.w; }
    
    int x[N], y[N];
    bitset<N> f[N]; // f[i][j]表示边(i,j)是否小于等于当前的边长
    
    inline int dis(int a, int b) { return abs(x[a] - x[b]) + abs(y[a] - y[b]); }
    
    void solve()
    {
        int n, m, cnt = 0, ans = 0;
        cin >> n >> m;
        for (int i = 1; i <= n; i++)
        {
            cin >> x[i] >> y[i];
            f[i].reset();
        }
        for (int i = 1; i <= n; i++)
            for (int j = i + 1; j <= n; j++)
                e[cnt++] = {i, j, dis(i, j)};
        sort(e, e + cnt, cmp);
        for (int i = 0; i < cnt; i++)
        {
            if (isprime[e[i].w]) ans += (f[e[i].x] ^ f[e[i].y]).count(); //只有0(大于当前边)和1(小于等于当前边)异或等于1
            f[e[i].x][e[i].y] = 1;
            f[e[i].y][e[i].x] = 1;
        }
        cout << ans << endl;
    }
    signed main()
    {
        ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
        get_prime(M - 1);
        int T;
        cin >> T;
        while (T--)
            solve();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    1008-Path

    题目大意:
    这道题的英文题面写得比较差,不建议看英文题面。
    有n个点和m条边,边有两种类型,一种是普通边,一种是特殊边。
    经过特殊边后可以以0的花费到达任何不相邻的点,以花费wi - k到达相邻的点。
    求出起始点到每个点的最短距离,如果到达不了输出-1。

    思路:
    首先这道题是最短路的问题,考虑用堆优化的dijkstra算法。
    当经过一条特殊边后,可以将所有与当前点不相邻的点进行更新,如果更新成功,那么更新后的值一定是这个点的最短距离,于是将更新后的点入队。
    因此建一个双层图,同时在队列中的状态要记录这个点的上一步是否经过了特殊边。同时用set来维护还没有确定最短距离的点。

    #include 
    using namespace std;
    #define rep(i, h, t) for (int i = h; i <= t; i++)
    #define dep(i, t, h) for (int i = t; i >= h; i--)
    #define ll long long
    const int N = 5e5;
    int n, m, s, k;
    ll dis[N][2];
    int gg[N];
    bool vis[N][2];
    struct re
    {
        ll a, b, p;
        bool operator<(const re x) const
        {
            return b > x.b;
        }
    };
    vector<re> ve[N];
    priority_queue<re> Q;
    int main()
    {
        ios::sync_with_stdio(false);
        int T;
        cin >> T;
        int cnt = 0;
        while (T--)
        {
            cin >> n >> m >> s >> k;
            rep(i, 1, m)
            {
                int x, y, z, zz;
                cin >> x >> y >> z >> zz;
                ve[x].push_back({y, z, zz});
            }
            set<int> S;
            rep(i, 1, n) if (i != s) S.insert(i);
            rep(i, 1, n) dis[i][0] = dis[i][1] = 1e18, vis[i][0] = vis[i][1] = 0;
            dis[s][0] = 0;
            Q.push({s, 0, 0});
            while (!Q.empty())
            {
    
                int x = Q.top().a;
                int yy = Q.top().p, y;
                Q.pop();
                if (vis[x][yy]) continue;
                vis[x][yy] = 1;
                S.erase(x);
                if (yy == 1)
                {
                    cnt++;
                    for (auto v : ve[x])
                    {
                        gg[v.a] = cnt;
                        if (dis[v.a][v.p] > dis[x][yy] + v.b - k)
                        {
                            dis[v.a][v.p] = dis[x][yy] + v.b - k;
                            Q.push({v.a, dis[v.a][v.p], v.p});
                        }
                    }
                    vector<int> ve;
                    for (auto v : S)
                    {
                        if (gg[v] != cnt)
                        {
                            dis[v][0] = dis[x][yy];
                            Q.push({v, dis[v][0], 0});
                            ve.push_back(v);
                        }
                    }
                    for (auto v : ve)
                        S.erase(v);
                }
                else
                {
                    for (auto v : ve[x])
                    {
                        if (dis[v.a][v.p] > dis[x][yy] + v.b)
                        {
                            dis[v.a][v.p] = dis[x][yy] + v.b;
                            Q.push({v.a, dis[v.a][v.p], v.p});
                        }
                    }
                }
            }
            rep(i, 1, n) ve[i].clear();
            rep(i, 1, n) if (min(dis[i][0], dis[i][1]) != 1e18) cout << min(dis[i][0], dis[i][1]) << " ";
            else cout << -1 << " ";
            cout << "\n";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
  • 相关阅读:
    Kong:高性能、插件化的云原生 API 网关 | 开源日报 No.62
    self-attention 李宏毅
    大模型Transformer笔记:KV缓存
    YOLOv8改进 | EIoU、SIoU、WIoU、DIoU、FocusIoU等二十余种损失函数
    栓Q:程序狗啃Flash AttentionV1 and V2
    CF770C Online Courses In BSU
    ICLR24大模型提示(1/11) | BadChain:大型语言模型的后门思维链提示
    Javascript 基础知识学习
    科技云报道:拉响警报!2023年三大网络安全威胁不容忽视
    一、nacos安装与高可用部署
  • 原文地址:https://blog.csdn.net/Shanhj/article/details/126482938