• 【济南集训】20191001解题报告


    (死亡。。)

    只有30分。。

    前两题还算正常,第一题数论+二分答案,我又又又把数组开爆了(生死看淡),第二题dp(其实我觉得模拟也还行,就是if太多成功把自己绕晕)

    第三题。。算了,先看题吧。。

    第一感觉肯定是暴力,这道题部分分挺多,k=0的情况可以用完全背包解决。(30分)

    没错我就是这一题有分。。

    第二感觉。。可能是dp

    然鹅,这题的正解是最短路,Dijkstra和SPFA都行。。

    我????(黑人问号)

    这是老师的解释

    这是老师的程序

    #include<bits/stdc++.h>
    #define A 20001
    #define N 5001
    using namespace std;
    typedef long long ll;
    struct node {
        int x; ll v;
        bool operator < (const node &oth) const {
            return v > oth.v;
        }
    };
    priority_queue<node>q;
    int pre[A], n, m, K, vis[A];
    ll a[N], dis[A], sum[N];
    ll getin() {
        ll s = 0; char c = getchar();
        while (c < '0' || c > '9') c = getchar();
        while (c <= '9' && c >= '0') s = s * 10ll + c - '0', c = getchar();
        return s;
    }
    void dijkstra() {
        for (int i = 1; i < a[1]; i++) dis[i] = 1e18;
        dis[0] = 0;
        q.push((node) {0, 0});
        while (!q.empty()) {
            int u = q.top().x;
            q.pop();
            if (vis[u]) continue;
            vis[u] = true;
            for (int i = 1; i <= n; i++) {
                int v = (u + a[i]) % a[1];
                if (dis[u] + a[i] < dis[v]) {
                    dis[v] = dis[u] + a[i];
                    pre[v] = i, q.push((node){v, dis[v]});
                }
            }
        }
    }
    int main() {
        freopen("equip.in", "r", stdin);
        freopen("equip.out", "w", stdout);
        scanf("%d%d%d", &n, &m, &K);
        for (int i = 1; i <= n; i++) a[i] = getin();
        dijkstra();
        for (int i = 1; i <= m; i++) {
            ll x = getin();
            if (x < dis[x % a[1]]) printf("No
    ");
            else {
                printf("Yes");
                if (K == 1) {
                    for (int j = 1; j <= n; j++) sum[j] = 0;
                    sum[1] += (x - dis[x % a[1]]) / a[1];
                    while (x % a[1]) {
                        sum[pre[x % a[1]]]++;
                        x = ((x - a[pre[x % a[1]]]) % a[1] + a[1]) % a[1];
                    }
                    for (int j = 1; j <= n; j++) printf(" %I64d", sum[j]);
                }
                printf("
    ");
            }
        }
        return 0;
    }

    (我理解无能。。。)

    所以!我想了一种更容易理解的方法!

    利用列不定方程求解

    对于每一个装备m,列方程有

    n1x1+n2x2+n3x3+……+nnxn=m

    求解就好

    (每一次把前n-1个看成一个整体利用扩展欧几里得求解就行)

    时间复杂度稍微比最短路大一点

    (程序有时间会写,调对了就放上来)

    时隔好长好长时间,终于看懂了老师的代码。。。(感谢机房dalao,鞠躬!)

    友情链接:(wsq%%%%)

    建出来的图大概长这样:

    1,2,3,4,5……表示当前的x对a[1]取模的余数

    再结合注释看一看:

    #include<bits/stdc++.h>
    #define A 20001
    #define N 5001
    using namespace std;
    typedef long long ll;
    struct node {
        int x; ll v;//x表示余数,v表示当前的最短路 
        bool operator < (const node &oth) const {
            return v > oth.v;
        }
    };
    priority_queue<node>q;//大根堆 
    int pre[A], n, m, K, vis[A];
    ll a[N], dis[A], sum[N];
    ll getin() {
        ll s = 0; char c = getchar();
        while (c < '0' || c > '9') c = getchar();
        while (c <= '9' && c >= '0') s = s * 10ll + c - '0', c = getchar();
        return s;
    }//快读 
    void dijkstra() {
        for (int i = 1; i < a[1]; i++) dis[i] = 1e18;//初始化 
        dis[0] = 0;
        q.push((node) {0, 0});//让第一个点入堆 
        while (!q.empty()) {
            int u = q.top().x;//u表示当前堆顶位置 
            q.pop();
            if (vis[u]) continue;
            vis[u] = true;//是否当前的堆顶已经被更新过了 
            for (int i = 1; i <= n; i++) {
                int v = (u + a[i]) % a[1];//与堆顶相连的点(能用已知碎片表示的价值) 
                if (dis[u] + a[i] < dis[v]) {
                    dis[v] = dis[u] + a[i];//更新 
                    pre[v] = i, q.push((node){v, dis[v]});//pre[i]表示从当前点到相连的点所用到的是第几号碎片
                    //这里用最短路处理过的dis数组表示可以用已知碎片表示的价值的最小值,每一个i表示不同的方案。// 
                }
            }
        }
    }
    int main() {
        freopen("equip.in", "r", stdin);
        freopen("equip.out", "w", stdout);
        scanf("%d%d%d", &n, &m, &K);
        for (int i = 1; i <= n; i++) a[i] = getin();
        dijkstra();
        for (int i = 1; i <= m; i++) {
            ll x = getin();
            if (x < dis[x % a[1]]) printf("No
    ");//为什么要x%a[1]呢?因为dis数组里存的是先对a[1]取模后的各种方案 
            else {
                printf("Yes");
                if (K == 1) {
                    for (int j = 1; j <= n; j++) sum[j] = 0;//初始化,sum[i]表示第i个碎片用了多少个 
                    sum[1] += (x - dis[x % a[1]]) / a[1];//处理sum[1] 
                    while (x % a[1]) {
                        sum[pre[x % a[1]]]++;//pre[x%a[1]]表示先从x中去掉a[1]*n后,再用哪一个碎片加上使得a[1]*n+a[i]=x 
                        x = ((x - a[pre[x % a[1]]]) % a[1] + a[1]) % a[1];//去掉当前x中所包含的a[1] 
                    }
                    for (int j = 1; j <= n; j++) printf(" %I64d", sum[j]);//输出 
                }
                printf("
    ");
            }
        }
        return 0;
    }

    一些难懂的问题:

    Q1:一定要取模a[1]吗?

    A1:不一定,对每一种碎片取模都可以,只是a[1]更方便。因为a[1]是最小价值的碎片,可以简化过程。

    Q2:为什么是dis[x%a[1]](详见主程序)?

    A2:因为dis数组中的下标是1,2,3,……,a[1]-1,表示的是先尽可能的用a[1]表示x,再去选用其他的,而对于每一个x=a[1]*n+……,n是一个定值,所以可以先不考虑n,转而去考虑除去a[1]*n后的价值应该怎么处理。这里也可以发现用取模a[1]的好处。

    Q3:x = ((x - a[pre[x % a[1]]]) % a[1] + a[1]) % a[1];为什么要再对a[1]取模?

    A3:先确定一个事实,x的值到现在一直是没有改变的。所以x=a[1]+n+……,所以要把a[1]从x中分离出来,避免影响结果。因为a[1]的使用数量已经在循环外处理过了。

    (RP++!)

  • 相关阅读:
    在Html中使用echarts图表
    html+css模拟微信对话
    解决React 的<img >src使用require的方式图片显示不出来,展示的是[object Module]的问题
    easygui入门
    python安装easygui
    关于gcc、make和CMake的区别
    FreeRTOS使用心得。
    C/C++整数输出位不足前补0方法
    AngularJS前端分页 + PageHelper后端分页
    AngularJS常见指令
  • 原文地址:https://www.cnblogs.com/Daz-Os0619/p/11616165.html
Copyright © 2020-2023  润新知