Codeforces Round #600 (Div. 2) E Antenna Coverage(DP)

題目鏈接:https://codeforces.com/contest/1253/problem/E

 

題目大意:

  有很多信號塔,每個信號塔的範圍是覆蓋的範圍是(aibi,ai+bi)(a_i-b_i,a_i+b_i),給出aia_ibib_i,每個信號塔可以花1塊錢變大b,1塊錢能增加1,最後求使得1m1-m都被覆蓋的最少硬幣數

 

題目思路:

  qsc講解視頻:傳送門
  上來看到過題人數蠻多,讀完題貪心搞了半天直接歇逼。。
  歇逼原因很簡單,先說明一下錯誤做法,也就是直接貪心做法! 貪心就是先根據左端點排序,然後根據右端點排序,然後發現當前能碰到的最右邊,到不了下一個點的最左邊,就說明中間需要補,補上就行。這樣爲啥錯了呢?因爲每個人都只顧着自己往右邊搭橋,但是!如果有個老鴿給右邊搭橋的時候,這個橋非常長,要注意,在往右邊搭橋的時候,它往左邊的橋也會延長! 所以,可能向左邊的那個橋,把之前那些點往右邊的橋蓋住了!一句話概括,剛纔有幾個向右的橋白建了!
  這可咋整呢?qsc說這個題看完根據過往題的經驗就能知道是dp。。還是太弱了。。確實,在想明白貪心的難點後,就很容易發現這個只能用dp,看到n這麼小也確實心動了想要nm複雜度,但是沒想好dp怎麼設。
  dp[i]dp[i]表示覆蓋1~i的最小代價。外層枚舉1~m,表示現在處理的是哪個位置,內層枚舉1~n,表示現在用哪個信號塔更新當前位置的dp值。接下來有兩種處理方法,區別非常小。
  第一種:一共三種情況
  第一種是當前枚舉的i在當前信號塔範圍內,那麼就說明當前信號塔已經能夠解決當前這個點,只需要繼承上一個點的最右解就行,直接躺贏,不需要付出任何代價,也就是
  dp[i]=min(dp[i],dp[i1])dp[i]=min(dp[i],dp[i-1])

  第二種是當前枚舉的i在當前信號塔範圍的右邊,那麼就說明當前點如果需要這個信號塔幫忙蓋住,就需要那個信號塔變強。咋變強呢,首先需要知道,需要變強多少,很明顯就是i到這個信號塔右界rr的距離,也就是iri-r,那麼這個信號塔增強後能夠覆蓋的範圍就是(l(ir),i)(l-(i-r),i),所以這一塊就可以交給這個信號塔幫忙撐着,前人僅需要負責包辦1~i-1的情況,所以就是
  dp[i]=min(dp[i],dp[max(0,a[j].l(ia[j].r))1]+ia[j].r)dp[i]=min(dp[i],dp[max(0,a[j].l-(i-a[j].r))-1]+i-a[j].r)

  第三種是當前枚舉的i在當前信號塔範圍的右邊,這個情況下,由於我們考慮的是1~i的情況,右邊的老鴿得蔓延到1纔行,這種情況等到這個老鴿的時候也能更新,所以就直接不這麼做,直接就當這個老鴿啥忙都幫不上,那咋整呢,只能自力更生,dp[i-1]是前人到達i-1的代價,只能讓前人辛苦辛苦再多花一塊錢繼續擴大,也就是
  dp[i]=min(dp[i],dp[i1]+1);dp[i]=min(dp[i],dp[i-1]+1);

  第二種:一共兩種情況
  這個情況裏面,就是砍掉了上面三種情況裏的第二種。這個弊病是啥呢,假如一個點一定要在信號塔右邊或者在信號塔覆蓋範圍內才更新的話,左邊有些點可能蓋不到,但是實際上這些點是需要更新的,比如信號塔要是最左邊的範圍也夠不到1,那麼需要最左邊的信號塔變強去夠到他。如何才能保證每個點都一定能被更新呢?很簡單,加進來一個在所有的東西都要左邊的點,在這裏就就是加入一個(0,0),這樣的話所有的點都能被更新,而使用的轉移方程就是上面的前兩種情況的dp方程。

 

以下是代碼:

第一種處理方法代碼:

#include<bits/stdc++.h>

using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define inf 0x3f3f3f3f
const int MAXN = 1e6+5;
struct node{
    int l,r;
}a[MAXN];
int n,m;
ll dp[MAXN];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n>>m){
        dp[0]=0;
        int x,y;
        rep(i,1,n){
            cin>>x>>y;
            a[i].l=x-y;
            a[i].r=x+y;
        }
        rep(i,1,m){
            dp[i]=1e9+7;
            rep(j,1,n){
                if(a[j].l<=i&&i<=a[j].r){
                    dp[i]=min(dp[i],dp[i-1]);
                }
                else if(a[j].r<i){
                    dp[i]=min(dp[i],dp[max(0,a[j].l-(i-a[j].r))-1]+i-a[j].r);
                }
                else{
                    dp[i]=min(dp[i],dp[i-1]+1);
                }
            }
        }
        cout<<dp[m]<<endl;
    }
    return 0;
}

第二種處理方法代碼:

#include<bits/stdc++.h>

using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define inf 0x3f3f3f3f
const int MAXN = 1e6+5;
struct node{
    int l,r;
}a[MAXN];
int n,m;
ll dp[MAXN];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n>>m){
        dp[0]=0;
        int x,y;
        rep(i,1,n){
            cin>>x>>y;
            a[i].l=x-y;
            a[i].r=x+y;
        }
        a[n+1].l=a[n+1].r=0;
        rep(i,1,m){
            dp[i]=1e9+7;
            rep(j,1,n+1){
                if(a[j].l<=i&&i<=a[j].r){
                    dp[i]=min(dp[i],dp[i-1]);
                }
                else if(a[j].r<i){
                    dp[i]=min(dp[i],dp[max(0,a[j].l-(i-a[j].r))-1]+i-a[j].r);
                }
            }
        }
        cout<<dp[m]<<endl;
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章