1. 程式人生 > 實用技巧 >20200718模擬賽4題解

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

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\n", l);
    return 0;
}

B. 乘車路線

題目描述

  • 編號為 \(1\sim 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\n", 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\n", 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\n", ans);
    return 0;
}