1. 程式人生 > >P3084 [USACO13OPEN]照片Photo (dp+單調佇列優化)

P3084 [USACO13OPEN]照片Photo (dp+單調佇列優化)

題目連結:傳送門

題目:

題目描述

Farmer John has decided to assemble a panoramic photo of a lineup of his N cows (1 <= N <= 200,000), which, as always, are conveniently numbered from 1..N. Accordingly, he snapped M (1 <= M <= 100,000) photos, each covering a contiguous range of cows: photo i contains cows a_i through b_i inclusive. The photos collectively may not necessarily cover every single cow.

After taking his photos, FJ notices a very interesting phenomenon: each photo he took contains exactly one cow with spots
! FJ was aware that he had some number of spotted cows in his herd, but he had never actually counted them. Based on his photos, please determine the maximum possible number of spotted cows that could exist in his herd. Output -1 if there is no possible assignment of spots to cows consistent with FJ'
s photographic results. 輸入輸出格式 輸入格式: * Line 1: Two integers N and M. * Lines 2..M+1: Line i+1 contains a_i and b_i. 輸出格式: * Line 1: The maximum possible number of spotted cows on FJ's farm, or -1 if there is no possible solution. 輸入輸出樣例 輸入樣例#15 3 1 4 2 5 3 4 輸出樣例#11 說明 There are
5 cows and 3 photos. The first photo contains cows 1 through 4, etc. From the last photo, we know that either cow 3 or cow 4 must be spotted. By choosing either of these, we satisfy the first two photos as well.
View Code

思路:

  如果要把牛放在第i個位置,它之前的那隻牛應該放在[li, ri]之間,根據輸入處理出li和ri,就可以轉移狀態了。

  讀入x,y時,用x更新ly+1,用x-1更新ry。

  讀入結束之後從前往後掃一遍,用li-1更新li;再從後往前掃一遍,用ri+1更新ri。

  然後就可以跑dp了,f[i] = max{f[j] | li ≤ j ≤ ri}

狀態:

  f[i] 表示把最後一隻牛放在第i個位置的最大數量。

狀態轉移方程:

  f[i] = max{f[j] | li ≤ j ≤ ri}

#include <bits/stdc++.h>

using namespace std;
const int MAX_N = 2e5 + 5;
#define tomax(a, b) a = a>b?a:b
#define tomin(a, b) a = a<b?a:b

int N, M, l[MAX_N], r[MAX_N];
int f[MAX_N];

int main()
{
//    freopen("testdata.in", "r", stdin);
    cin >> N >> M;
    for (int i = 1; i <= N+1; i++)
        r[i] = i-1;
    for (int i = 1; i <= M; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        tomin(r[y], x-1);
        tomax(l[y+1], x);
    }
    for (int i = 2; i <= N+1; i++)
        tomax(l[i], l[i-1]);
    for (int i = N; i >= 1; i--)
        tomin(r[i], r[i+1]);
    memset(f, -1, sizeof f);
    f[0] = 0;
    for (int i = 1; i <= N+1; i++)
        for (int j = l[i]; j <= r[i]; j++) if(f[j] != -1)
            tomax(f[i], f[j] + (i!=N+1 ? 1 : 0));

    cout << f[N+1] << endl;
    return 0;
}
/*
5 3
1 4
2 4
1 1
*/
View Code

 

本來是瞄了一眼題解,理解了思路之後準備不優化暴力T一發的,結果直接AC了,還跑得賊快?-。=

不過這樣子寫應該可以被兩隻牛的大資料卡掉:

200000 2
1 100000
100001 200000
View Code

 

獻上單調佇列優化的正解:

#include <bits/stdc++.h>

using namespace std;
const int MAX_N = 2e5 + 5;
#define tomax(a, b) a = a>b?a:b
#define tomin(a, b) a = a<b?a:b

int N, M, l[MAX_N], r[MAX_N];
int h, t, q[MAX_N], f[MAX_N];

int main()
{
    cin >> N >> M;
    memset(f, 0, sizeof f);
    for (int i = 1; i <= N+1; i++)
        r[i] = i-1;
    for (int i = 1; i <= M; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        tomin(r[y], x-1);
        tomax(l[y+1], x);
    }
    for (int i = 2; i <= N+1; i++)
        tomax(l[i], l[i-1]);
    for (int i = N; i >= 1; i--)
        tomin(r[i], r[i+1]);
    int j = 1;
    h = 1, t = 0, q[++t] = 0;
    for (int i = 1; i <= N+1; i++) {
        while (j <= N && j <= r[i]) {
            if (f[j] == -1) {
                ++j;
                continue;
            }
            while (h <= t && f[q[t]] <= f[j]) --t;
            q[++t] = j;
            ++j;
        }
        while (h <= t && q[h] < l[i]) ++h;
        if (h <= t) f[i] = f[q[h]] + (i!=N+1 ? 1 : 0);
        else f[i] = -1;
    }
    cout << f[N+1] << endl;
    return 0;
}
View Code