CF 559E

題目大意

在一條(,+) 的數軸上,有N 盞探照燈,座標分別爲ai 。每盞探照燈都可以往負方向或正方向發射光線,但每盞燈都有一個強度li ,也就是說他最多隻能照到(aili,ai)(ai,ai+li) 。問你,在合法的規劃每盞燈的方向下,最多能照到多少長度的數軸。一段數軸(l,r) 的長度爲rl ,他被照到當且僅當整段都被某些探照燈所覆蓋。

數據範圍

N100,ai,li108

題解

我們先將探照燈按ai 排序。
首先要注意,假如一段區間(l,r) 被覆蓋了,那麼必然是被一段連續的探照燈([i,j] 所覆蓋的。
Fi,j,k 表示當前用了前i 盞燈,最後一段是探照燈j 貢獻的,其方向爲k 最多能照到多少長度。當k=0 時表示照向負方向,k=1 時表示照向正方向。
假設我們已經知道了Fi,j,k ,接着要進行轉移。
Pre 表示當前覆蓋區間的右端點,可以直接用aj+klj 計算出來。我們嘗試枚舉一個p ,表示要把前p 盞燈用完,並且p 要對答案有貢獻,也就是說,在最終的方案中,p 能唯一(或稱爲最先)覆蓋某一段燈。而且p 枚舉時應當兩個方向都枚舉。
那麼我們可以畫一幅圖,
這裏寫圖片描述
Pre 就是之前的燈照的區間,Far 表示當前轉移枚舉的右端點,那麼有用的P 就如圖中的P1,P2 所示,必須要把某一段Pre,Far 之間的空白填補。設P 的右端點爲Nr 。那麼P 造成的貢獻就是min(Lp,NrPre) ,最後再考慮上Far,P1 之間的覆蓋,P 的貢獻就是min(Lp,NrPre)+FarNr 。因此,這種轉移是
Fp,far,dir=Fi,j,k+min(Lp,NrPre)+FarNr

這裏也體現了我們一開始將燈按ai 排序的優勢。我們的枚舉是單調的,而且我們可以輕鬆解決這種情況
這裏寫圖片描述

最後我們可以得到一個O(N3) 的算法。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 105;

struct Node
{
    int x,l;
}A[MAXN];

int F[MAXN][MAXN][2],N,Ans;

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

int main()
{
    //freopen("data.in","r",stdin),freopen("data.out","w",stdout);
    scanf("%d", &N);
    for(int i = 1;i <= N;i ++) scanf("%d%d", &A[i].x, &A[i].l);
    sort(A + 1,A + N + 1,cmp);
    A[0].x = -(1 << 30);
    for(int i = 0;i <= N;i ++)
        for(int j = 0;j <= i;j ++)
            for(int p = 0;p < 2;p ++)
            {
                Ans = max(Ans,F[i][j][p]);
                int Pr = A[j].x + p * A[j].l;
                for(int k = i + 1,mx = -(1 << 30),a,b;k <= N;k ++)
                    for(int d = 0;d < 2;d ++)
                    {
                        int nxt = A[k].x + d * A[k].l;
                        if (nxt > mx) mx = nxt,a = k,b = d;
                        F[k][a][b] = max(F[k][a][b],F[i][j][p] + min(A[k].l,nxt - Pr) + mx - nxt);
                    }
            }
    printf("%d\n", Ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章