zoukankan      html  css  js  c++  java
  • 20200718模拟赛4题解

    A. 渔民的烦恼

    题目描述

    • 在某个海边小国,大多数居民都是渔民,这个国家的所有城镇都沿直线分布在海边。渔民们捕获大量的海鱼,但就象世界上大多数的渔民一样,他们并不喜欢吃鱼,所以他们决定从邻国收养一些贫困家庭的小孩,让他们来帮着吃鱼,国家规定每个城镇收养的贫困儿童数量必须相等。
    • 一条又长又直的公路贯穿整个海岸将所有的城镇连接了起来,所以每个城镇(除去第一个和最后一个)都直接和相邻的两个城镇相连接。一个小孩一年要吃掉一吨鱼,每个城镇捕获的鱼既可以在本地吃也可以运往其它城市吃,在运输过程中,每公里要上交一吨鱼作为过路费。
    • 已知每个城镇一年的捕鱼产量,并假设运输方案是最佳的,计算最多能收奍多少个贫困儿童。

    输入格式

    • 第一行包含一个整数 N,其中 (1≤N≤10^5),表示城镇总数。
    • 接下来的 N 行每行包含两个整数 A 和 B,其中 (1≤A≤10^9,0≤B≤10^9),分别表示城镇的位置(坐标)和该城镇的捕鱼产量,所有城镇按其位置从小到大排序给出,注意问题一定存在正整数解。

    输出格式

    • 输出文件仅一行,包含一个整数表示每个城镇最多能够收养的贫困儿童数量。

    样例输入

    4
    20 300
    40 400
    340 700
    360 600
    

    样例输出

    415
    

    Solve

    • 二分答案,考试对拍时还以为会炸,后来才醒悟过来,log 1e9 也就30,只是有的地方没开long long炸掉了,直接宏定义...

    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    #define int long long
    using namespace std;
    const int N = 1e5+5;
    int n, a[N], w[N], c[N], l = 1<<30, r;
    ll s;
    bool judge(int x) {
        memset(c, 0, sizeof(c));
        for (int i = 1; i < n; ++i) {
            if (w[i] + c[i] < x) c[i+1] -= x - (w[i] + c[i]) + a[i+1] - a[i];
            else if (w[i] + c[i] > x && w[i] + c[i] - x > a[i+1] - a[i]) c[i+1] += w[i] + c[i] - x - a[i+1] + a[i];
        }
        return w[n] + c[n] >= x;
    }
    signed main() {
        scanf("%lld", &n);
        for (int i = 1; i <= n; ++i)
            scanf("%lld%lld", &a[i], &w[i]), 
            l = min(l, w[i]), s += w[i];
        r = s / n + 1;
        while (l < r) {
            int mid = l+r+1 >> 1;
            if (judge(mid)) l = mid;
            else r = mid - 1;
        }
        printf("%lld
    ", l);
        return 0;
    }
    

    B. 乘车路线

    题目描述

    • 编号为 (1sim N) 的 N 座城镇用若干仅供单向行驶的道路相连,每条道路上均有两个参数:道路长度(length)和在该条道路上行驶的费用(cost)。
    • BOB 准备从城镇 1 出发到达城镇 N,但他目前只有 W 的钱,为此,你需要帮助他寻找一条从城镇 1 到城镇 N 在他能支付的前提下的一条最短路线。

    输入格式

    • (W N M)(N为城镇数目,2<=N<=100,M 为道路条数,1<=M<=10000,W 为钱的数目,0<=W<=1000)
    • 随后的 M 行每行为一条道路的信息,包含 4 个数值(u,v,w,cost),表示从城镇 u 到 v 有一条长度为 w 的单向道路,经过这条道路需要花费 cost 。(1<=u,v<=N,1<=w<=100,0<=cost<=100)

    输出格式

    • 输出最短长度,若无解,则输出“NO”;

    样例输入

    5 6 7
    1 2 2 3
    2 4 3 3
    3 4 2 4
    1 3 4 1
    4 6 2 1
    3 5 2 0
    5 4 3 2
    

    样例输出

    11
    

    Solve1

    • 典型的二维spfa.二维spfa是什么鬼,确实没听说过
    • 添加一维记录费用

    Code1

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N = 105, M = 1005, MN = 0x3f3f3f3f;
    struct Side {
        int t, d, w, next;
    }e[10005];
    int head[N], tot;
    void Add(int x, int y, int z, int w) {
        e[++tot] = (Side) {y, z, w, head[x]};
        head[x] = tot;
    }
    int W, n, m, d[N][M], ans = MN;
    bool v[N][M];
    queue< pair<int, int> > q;
    void Spfa(int u) {//基本上算是板子
        memset(d, 0x3f, sizeof(d));
        d[u][0] = 0;
        q.push(make_pair(u, 0));
        while (!q.empty()) {
            int x = q.front().first, s = q.front().second;
            q.pop(); v[x][s] = 0;
            for (int i = head[x]; i; i = e[i].next) {
                int y = e[i].t, w = s + e[i].w;
                if (w > W) continue;
                if (d[y][w] > d[x][s] + e[i].d) {
                    d[y][w] = d[x][s] + e[i].d;
                    if (v[y][w]) continue;
                    v[y][w] = 1;
                    q.push(make_pair(y, w));
                }
            }
        }
    }
    int main() {
        scanf("%d%d%d", &W, &n, &m);
        while (m--) {
            int x, y, z, w;
            scanf("%d%d%d%d", &x, &y, &z, &w);
            Add(x, y, z, w);
        }
        Spfa(1);
        for (int i = 0; i <= W; ++i)
            ans = min(ans, d[n][i]);
        if (ans == MN) puts("NO");
        else printf("%d
    ", ans);
        return 0;
    }
    

    Solve2

    • 为什么正解都放出来还又搞了一个呢?自然是比正解优
    • 暴力出奇迹!

    Code1 : 总时间 258 ms, 内存 1268 KiB.
    Code2 : 总时间 67 ms, 内存 500 KiB.

    Code2

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 105;
    struct Side {
        int t, d, w, next;
    }e[10005];
    int head[N], tot;
    void Add(int x, int y, int z, int w) {
        e[++tot] = (Side) {y, z, w, head[x]};
        head[x] = tot;
    }
    int n, m, ans = 1<<30, W;
    bool v[N];
    void Dfs(int x, int s, int d) {
        if (s > W || d >= ans) return;
        if (x == n) {
            ans = min(ans, d);
            return;
        }
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].t;
            if (v[y]) continue;
            v[y] = 1;
            Dfs(y, s + e[i].w, d + e[i].d);
            v[y] = 0;
        }
    }
    int main() {
        scanf("%d%d%d", &W, &n, &m);
        while (m--) {
            int x, y, z, w;
            scanf("%d%d%d%d", &x, &y, &z, &w);
            Add(x, y, z, w);
        }
        v[1] = 1; 
        Dfs(1, 0, 0);
        if (ans == 1<<30) puts("NO");
        else printf("%d
    ", ans);
        return 0;
    }
    

    Cloakroom

    题目描述

    • 有n件物品,每件物品有三个属性 a[i],b[i],c[i] (a[i]<b[i])。
    • 个询问,每个询问由非负整数 m,k,s 组成,问是否能够选出某些物品使得:
      1. 对于每个选的物品 i,满足 a[i]<=m 且 b[i]>m+s。
      2. 所有选出物品的 c[i]的和正好是 k。

    输入格式

    • 第一行一个正整数 n(n<=1,000),接下来 n 行每行三个正整数,分别表示 c[i],a[i],b[i] (c[i]<=1,000,1<=a[i]<b[i]<=109)。
    • 下面一行一个正整数 q(q<=1,000,000),接下来 q 行每行三个非负整数 m,k,s(1<=m<=(10^9),1<=k<=100,000,0<=s<=(10^9))。

    输出格式

    • 输出 q行,每行为 "TAK "(yes)或"NIE"(no),第 i 行对应第 i 次询问的答案。

    样例输入

    5
    6 2 7
    5 4 9
    1 2 4
    2 5 8
    1 3 9
    5
    2 7 1
    2 7 2
    3 2 0
    5 7 2
    4 1 5
    

    样例输出

    TAK
    NIE
    TAK
    TAK
    NIE
    

    Solve

    Code

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    struct Node1 {
        int a, b, c;
        bool operator < (const Node1 &b) const {
            return a < b.a;
        }//重载运算符,对物品按a值从小到大排序
    }a[1005];
    struct Node2 {
        int m, k, s, id;
        bool operator < (const Node2 &b) const {
            return m < b.m;
        }/重载运算符,对询问按m值从小到大排序
    }b[1000005];
    int n, q, f[100005];//f数组题解中的加粗部分进行了详细的解释
    bool ans[1000005];
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            scanf("%d%d%d", &a[i].c, &a[i].a, &a[i].b);
        scanf("%d", &q);
        for (int i = 1; i <= q; ++i)
            scanf("%d%d%d", &b[i].m, &b[i].k, &b[i].s), b[i].id = i;
           //记录编号id,离线排序后便于保存答案
        sort(a + 1, a + n + 1);//对物品按a值从小到大排序
        sort(b + 1, b + q + 1);//对询问按m值从小到大排序
        f[0] = 1 << 30;//初始化f[0]为极大值,防止在运行 min(f[k-a[j].c], a[j].b)时出现结果都是0的情况
        for (int i = 1, j = 1; i <= q; ++i) {
            for (; j <= n && a[j].a <= b[i].m; ++j)//满足条件1:a<=m
                for (int k = 100000; k >= a[j].c; --k)
                    f[k] = max(f[k], min(f[k-a[j].c], a[j].b));
            if (f[b[i].k] > b[i].m + b[i].s) ans[b[i].id] = 1;
            //满足条件3:c之和==k
            //满足条件2:b>m+s
        }
        for (int i = 1; i <= q; ++i)
            puts(ans[i] ? "TAK" : "NIE");
        //三目运算符,个人比较喜欢使用,挺方便
        return 0;
    }
    

    D. 星际网络

    题目描述

    • LY星系有很多个星球。它们之间通过一个巨大的互联网进行通讯。随着信息时代的发展,旧的网络已经不能满足需求,于是LY星系决定建设一个新的网络。
    • LY星系有很多个星球,有些星球一天有几十个小时,有些星球一天只有几个小时。但每个星球的一个小时都是一样长的。因此每个星球一天的长短都不一样,这就导致了一个问题:星球上的生物都是在白天工作夜晚 休息,因此每个星球每天都有上网高峰期和低峰期,当很多星球同时达到高峰期时,网络便会变得异常拥堵,进而带来延迟。所以LY星系需要建设一个有足够大带宽的网络来避免这一问题。现在他们想知道,网络在一个小时内的最大流量是多少。

    输入格式

    • 输入数据的第一行为一个正整数N,表示LY星系共有N个星球。
    • 接下来N行,每行描述一个星球。对于每个星球的描述,开头为两个正整数D,T,表示这个星球一天有D个小时,当前位于T时刻(即某一天已经过去了T小时).
    • 接下来是D个正整数(q_i) ,表示第i小时到第i+1小时的流量。

    输出格式

    • 输出共一行,为一个整数Ans,表示一个小时内的最大流量。

    样例输入

    2
    4 0 1 2 3 4
    2 0 3 1
    

    样例输出

    6
    

    数据范围与提示

    • 在 4n+3 时刻,流量=3+3=6 达到最大。
    • 对于 10% 的数据,N<=10;
    • 对于 20% 的数据,N<=100;
    • 对于 40% 的数据,N<=10000;
    • 对于 70% 的数据,N<=50000;
    • 对于全部的数据,N<=100000,0<=T<D<=24,(q_i)<=1000000;

    Solve

    • 其实n的范围没啥用,总共就24种星球(太妙了呀)时长为13,17,19,22的星球天数与其他所有天数互质,说明最大的流量是一定可以选了,单独处理会更优,余下的天数最小公倍数为55440,55440天一个循环,只需要遍历这55440天就可以了。

    Code

    #include <cstdio>
    #include <algorithm>
    #define ll long long
    using namespace std;
    const int N = 30, M = 55440;//1~24中除去13,17,19,23的数的最小公倍数
    int n, l[N];
    ll a[N][N], ans;
    void Add(int x) {
        ll m = 0;
        for (int i = 0; i < x; ++i)
            m = max(m, a[x][i]);
        ans += m;
    }
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            int d, s;
            scanf("%d%d", &d, &s);
            for (int i = 0; i < d; ++i)
                scanf("%d", &l[i]);
            for (int i = 0; i < d; ++i)
                a[d][i] += l[(i+s)%d];    
        }
        for (int j = 0; j < M; ++j) {
            ll s = 0;
            for (int i = 1; i <= 24; ++i)
                if (i != 13 && i != 17 && i != 19 && i != 23)
                    s += a[i][j%i];
            ans = max(ans, s);
        }
        Add(13); Add(17); Add(19); Add(23);
        printf("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    爬楼梯
    字母异位词分组
    发射子弹过程
    删除元素
    删除排序数组中的重复项
    JAVA编程-------------9、查找1000以内的完数
    JAVA-------------8、计算a+aa+aaa+.....
    JAVA编程----------7、统计一段字符串中的英语字母数,空格数,数字和其他字符数
    JAVA编程---------6、最大公约数和最小公倍数
    JAVA编程----------5、利用条件运算符的嵌套来完成:成绩>=90(A) 60-89(B) 60分以下(C)
  • 原文地址:https://www.cnblogs.com/Z8875/p/13337291.html
Copyright © 2011-2022 走看看