ICPC North Central NA Contest 2018

ICPC North Central NA Contest 2018

待補node

  1. A後綴數組
  2. H機率

1. 題目分析

  • A:後綴數組待補
  • B: dp版題
  • C: 數論--小學數學奧賽
  • D: 大模擬
  • E: 簽到
  • F: 思惟簽到
  • G: 有條件的最短路問題--卡內存,交換數組第二維和第三維
  • H: 機率待補
  • I: 羊狼菜思惟題
  • J: 數學題--故意給出很大範圍,實際用不到那麼大範圍

2. 題解

A.Pokegene

後綴數組ios

B.Maximum Subarrays

C.Rational Ratio

題意:給定一個循環小數的前半部分和循環節長度,該前半部分由不循環的部分和循環部分組成,循環部分只出現一次。求該小數對應的分數
題解:小學數學奧數題,把一個小數轉換爲分數的方法以下:c++

  1. 先把小數的整數部分拆出來,記爲x
  2. 把小數部分的不循環部分拆出來,記爲y
  3. 把小數部分的循環部分拆出來,記爲z
    那麼小數對應的分數爲: x + (y - z) / (9..9(z個9)0..0(y個0))
    例如:1.6 1 => x = 1, y = 0, z = 6,則1 + (6-0) / 9 = 5 / 3
    123.456 2 => x = 123, y = 4, z = 56, 則123 + (456 - 4) / 990 = 61111/495
#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    string s;
    int len;
    cin >> s >> len;
    int idx = s.find(".");
    string first = "0", second = "0";
    first = s.substr(0, idx);
    second = s.substr(idx + 1);
    // cout << first << " " << second << endl;
    int len_second = second.size();
    LL mother = 0;
    for (int i = 1; i <= len; ++i) mother = mother * 10 + 9;
    for (int i = 1; i <= len_second - len; ++i) mother *= 10;
    // cout << mother << endl;
    LL son = atoll(second.c_str()) - atoll(second.substr(0, len_second - len).c_str());
    // cout << son;
    LL gcd = __gcd(mother, son);
    // cout << first << endl;
    mother /= gcd, son /= gcd;
    cout << son + mother * atoll(first.c_str()) << "/" << mother;
    return 0;
}

D.Travel the Skies

題意:有 k 個機場,總共運行 n 天,有 m 架飛機,每架飛機有四個屬性:u 表明起點,v 表明終點,d 表明哪天
起飛,z 表示飛機的容量 (能夠乘多少人),在第 b 天,有 c 名但願旅遊的乘客到達 a 機場,你能夠安排旅客的起飛日
期 (到達日期當天或者以後的任何一天) 和目的地,問你是否可以保證每架飛機都可以裝滿,每架飛機飛行花費一天時
間,一架飛機只飛行一次但乘客能夠飛行屢次
題解:按照題意直接模擬便可,idx[a][d] 存儲在 a 機場第 d 天起飛的飛機編號,cnt[a][d] 則表示在 a 機場第 d 天
的人數,從前向後遍歷每一天,對於每一天遍歷全部機場,儘可能將每一架飛機裝滿,因爲乘客能夠屢次飛行,因此須要
將每架飛機目的地次日的人數加上這架飛機上的乘客數,若是某一天某個機場還有乘客沒有起飛,則直接留到第二
數組

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>

using namespace std;

const int N = 1510;
const int M = 15;

struct node {
    int u, v, d, c;
};

int k, n, m;
int cnt[M][M];
vector<int> idx[M][M];
node p[N];

int main()
{
    scanf("%d%d%d", &k, &n, &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d%d", &p[i].u, &p[i].v, &p[i].d, &p[i].c);
        idx[p[i].u][p[i].d].push_back(i);
    }
    for (int i = 1; i <= k * n; i++) {
        int a, d, t;
        scanf("%d%d%d", &a, &d, &t);
        cnt[a][d] = t;
    }
    for (int d = 1; d <= n; d++) {
        for (int a = 1; a <= k; a++) {
            int len = (int)idx[a][d].size();
            for (int b = 0; b < len; b++) {
                int id = idx[a][d][b];
                int t = min(cnt[a][d], p[id].c);
                cnt[a][d] -= t;
                p[id].c -= t;
                cnt[p[id].v][d + 1] += t;
            }
            cnt[a][d + 1] += cnt[a][d];
        }
    }
    int flag = 1;
    for (int i = 1; i <= m; i++)
        if (0 != p[i].c) flag = 0;
    if (1 == flag) printf("optimal\n");
    else printf("suboptimal\n");
    return 0;
}

E. Euler's Number

題意:輸入 n,計算 e
思路:簽到題ide

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

int n;

double euler(int n)
{
    double res = 0, fac = 1.0;
    for (int i = 0; i <= n; i++) {
        if (0 == i) res += fac;
        else {
            fac /= i;
            res += fac;
        }
    }
    return res;
}

int main()
{
    scanf("%d", &n);
    printf("%.15lf\n", euler(n));
    return 0;
}

F. Lipschitz Constant

題意:給定二維平面上 n 個點,求任意兩點間斜率絕對值的最大值
題解:斜率絕對值的最大的兩個點必定是 x 座標相鄰的兩個點,按照 x 座標排序,選擇相鄰的點計算斜率
證實:用反證法證實,如圖,假設 AB 斜率絕對值最大,而且 AB 不相鄰,若是 AB 之間存在一點 C 在 AB 直線 的下方,顯然 kBC > kAB,若是 AB 之間存在另外一點 D 在 AB 上方,同理會有 kDA > kAB,因此假設不成立,即斜率絕對值的最大的兩個點必定是 x 座標相鄰的兩個點spa

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

const int N = 200010;

struct node {
    double x, y;
};

node p[N];
int n;

bool cmp(node a, node b)
{
    return a.x < b.x;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%lf%lf", &p[i].x, &p[i].y);
    sort(p + 1, p + n + 1, cmp);
    double res = 0;
    for (int i = 2; i <= n; i++) {
        double t = abs(p[i].y - p[i - 1].y) / abs(p[i].x - p[i - 1].x);
        res = max(res, t);
    }
    printf("%.9lf\n", res);
    return 0;
}

G. Tima goes to Xentopia

題意:有一張無向圖N個點M條邊,每條邊有顏色,可能爲白,紅,藍,同時每條邊還有一個邊權。小A從1號點出發走到n號點,問是否存在一條通過k1條紅邊,k2條藍邊的最短路,若是有,輸出其最短路權值;若是沒有,輸出-1
N ~ 450, M ~ 1100, K1 * K2 <= 800
題解:
本題求解一個帶有條件的最短路,是一個模板題。可是若是開始dis[450][800][800],那麼mle,所以由k1*k2<=800,則dis[450][800][100],首先要判斷k1和k2的大小,小的當成k2.有條件的最短路和沒條件的最短路區別在於放入st數組和dis數組設置。code

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<LL, int> PLI;
LL const INF = 0x3f3f3f3f3f3f3f3f;

struct point {
    int id, r, b;
    LL dis;
    point(int _id, int _r, int _b, LL _dis) : id(_id), r(_r), b(_b), dis(_dis) {}
    bool operator < (const point & t) const {
        return dis > t.dis;
    }
};
// int const N = 460, M = 1110;
LL dis[500][810][100];
int st[500][810][100];
int k1, k2, n, m, s, t, idx, e[3000], ne[3000], h[500], w[3000], color[3000];

void add(int a, int b, int c, int col) {
    e[idx] = b, w[idx] = c, color[idx] = col, ne[idx] = h[a], h[a] = idx++;
}

void Dijkstra() {
    memset(dis, 0x3f, sizeof dis);
    priority_queue<point> q;
    dis[s][0][0] = 0;
    q.push({s, 0, 0, 0});
    while(!q.empty()) {
        point temp = q.top();
        q.pop();
        if(st[temp.id][temp.r][temp.b])  continue;  // 記錄的時候要記點,邊1,邊2
        st[temp.id][temp.r][temp.b] = 1;
        for(int i = h[temp.id]; ~i; i = ne[i]) {
            point t = {e[i], temp.r + (color[i] == 1), temp.b + (color[i] == 2), temp.dis + w[i]};
            if(t.r > k1 || t.b > k2)    continue;
            if(t.dis < dis[t.id][t.r][t.b]) {
                dis[t.id][t.r][t.b] = t.dis;
                q.push(t);
            }
        }
    }
    printf("%lld\n", dis[t][k1][k2] == INF ? -1 : dis[t][k1][k2]);
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);

    memset(h, -1, sizeof h);
    cin >> n >> m >> k1 >> k2;
    bool change = false;
    if (k1 < k2) {
        swap(k1, k2);
        change = true;
    }
    for (int i = 1, a, b, weight, colorr; i <= m; ++i) {
        cin >> a >> b >> weight >> colorr;
        if (change) colorr = 3 - colorr;
        add(a, b, weight, colorr);
        add(b, a, weight, colorr);
    }
    cin >> s >> t;
    // cout << s << " " << t << endl;

    Dijkstra();
    return 0;
}

H. New Salaries

機率待補排序

I. Other Side

題意: 農夫有W匹狼,S只羊,C顆菜,當農夫不在場的時候狼會吃羊,羊會吃菜,但狼不吃菜。船的最大容量是K,農夫要將貨物運到河對岸,問貨物是否收到損失
題解:
羊必須和狼和菜分開,將狼和菜當作一個總體,狼和菜一塊兒運,或者是隻運羊。
(1)若是S < K || W + C < K,Yes
船上能夠裝下所有的羊,或者能夠裝下所有的狼和菜,能夠把少的一方一直放在船上,把多的一方分多趟運到對岸。
(2)若是S == K && W + C <= 2 * K
船上放上所有的羊就滿了,先把 K 只羊運到對岸,再把 K 個狼和菜運到對岸,再將羊運回起點,將剩下的狼和菜所有運到對岸,最後再運羊。能夠發現,當W + C <= 2 * K時能夠成功。ip

(3)若是 W + C == K && S <= 2 * K內存

#include <bits/stdc++.h>

using namespace std;

int main() {
    int w, c, s, k;
    cin >> w >> s >> c >> k;
    int flg = 0;
    if (s < k || w + c < k) flg = 1;
    if (s == k && w + c <= 2 * k) flg = 1;
    if (s <= 2 * k && w + c == k) flg = 1;
    if (flg) cout << "YES";
    else cout << "NO";
    return 0;
}

J.Kaleidoscopic Palindromes

題意:求區間[a, b]內的全部數字中,2~k進制都是迴文串的數字個數
a~2e6, k~1e5
題解:因爲可以知足2~k進制都是迴文串的數字個數很是少,所以只須要直接暴力枚舉便可

#include <bits/stdc++.h>

using namespace std;

int a, b, k;

bool check(int x, int base) {
    int pal[100], cnt = 0;
    while (x) {
        pal[cnt ++] = x % base;
        x /= base;
    }
    
    for (int i = 0; i < cnt; ++i) {
        if (i < cnt - i - 1 && pal[i] != pal[cnt - i - 1]) return false;
    }
    return true;
}

int main () {
    cin >> a >> b >> k;
    int sum = 0;
    for (int i = a; i <= b; ++i) {
        int j;
        for (j = 2; j <= k; ++j) {
            if (!check(i, j)) break;
        }
        
        if (j == k + 1) sum ++;
    }
    cout << sum;
    return 0;
}
相關文章
相關標籤/搜索