[Codeforces #608 div2]1271D Portals

Description

You play a strategic video game (yeah, we ran out of good problem legends). In this game you control a large army, and your goal is to conquer n n castles of your opponent.html

Let’s describe the game process in detail. Initially you control an army of k k warriors. Your enemy controls n castles; to conquer the i i -th castle, you need at least ai warriors (you are so good at this game that you don’t lose any warriors while taking over a castle, so your army stays the same after the fight). After you take control over a castle, you recruit new warriors into your army — formally, after you capture the i i -th castle, b i b_i warriors join your army. Furthermore, after capturing a castle (or later) you can defend it: if you leave at least one warrior in a castle, this castle is considered defended. Each castle has an importance parameter c i c_i , and your total score is the sum of importance values over all defended castles. There are two ways to defend a castle:
if you are currently in the castle i i , you may leave one warrior to defend castle i i ;
there are m m one-way portals connecting the castles. Each portal is characterised by two numbers of castles u u and v v (for each portal holds u > v u>v ). A portal can be used as follows: if you are currently in the castle u, you may send one warrior to defend castle v v .
Obviously, when you order your warrior to defend some castle, he leaves your army.web

You capture the castles in fixed order: you have to capture the first one, then the second one, and so on. After you capture the castle i i
(but only before capturing castle i+1) you may recruit new warriors from castle i i , leave a warrior to defend castle i i , and use any number of portals leading from castle i i to other castles having smaller numbers. As soon as you capture the next castle, these actions for castle i i won’t be available to you.數組

If, during some moment in the game, you don’t have enough warriors to capture the next castle, you lose. Your goal is to maximize the sum of importance values over all defended castles (note that you may hire new warriors in the last castle, defend it and use portals leading from it even after you capture it — your score will be calculated afterwards).
Can you determine an optimal strategy of capturing and defending the castles?app

Input

The first line contains three integers n , m a n d k ( 1 n 5000 , 0 m m i n ( n ( n 1 ) 2 , 3 1 0 5 ) , 0 k 5000 n, m and k (1≤n≤5000, 0≤m≤min(\frac{n(n−1)}{2},3⋅10^5), 0≤k≤5000 ) — the number of castles, the number of portals and initial size of your army, respectively.ide

Then n n lines follow. The i i -th line describes the i i -th castle with three integers a i a_i , b i b_i and c i ( 0 a i , b i , c i 5000 ) c_i (0≤a_i,b_i,c_i≤5000) — the number of warriors required to capture the i i -th castle, the number of warriors available for hire in this castle and its importance value.svg

Then m m lines follow. The i i -th line describes the i i -th portal with two integers u i u_i and v i ( 1 v i < u i n ) v_i (1≤v_i<u_i≤n) , meaning that the portal leads from the castle u i u_i to the castle v i v_i . There are no two same portals listed.優化

It is guaranteed that the size of your army won’t exceed 5000 under any circumstances (i. e. k + i = 1 n b i 5000 k+∑_{i=1}^{n}b_i≤5000 ).ui

Output

If it’s impossible to capture all the castles, print one integer 1 −1 .
Otherwise, print one integer equal to the maximum sum of importance values of defended castles.this

題意

按順序佔領 1 n 1-n 全部城堡,能夠選擇在該城堡剛被佔領時留下一個士兵得到權值,也能夠經過 u , v u,v 單向邊來在後面佔領該城堡得到權值。spa

思路


update at 19/12/19

以前的作法過於混亂(甚至我本身都以爲像是瞎蒙的),所以借鑑了其餘神犇的題解仔細思考後發現可使用反悔型貪心來作這道題。
思路爲:
一路掃過去,每次都假定留守全部可留守的點,發現當前的士兵不夠則從已留守的點中選取權值最小的放棄留守。
依然是基於 靠後留守更優 策略,預處理出每一個點可留守的集合,遍歷到該點直接將可留守的所有push到已留守集合中,再遍歷下一個點。 在遍歷點過程當中發現士兵不夠,pop已留守集合,若是pop空了還不夠,說明遊戲失敗。
代碼一塊兒放到後面了。自我感受註釋夠詳細了。


能不能佔領完城堡很好判斷,只須要每次都取光士兵而不留守掃一遍,若是這種狀況下都有沒法佔領的城堡,那必定無法佔領完。
若是要留守城堡 i i ,那麼必定是在最後留守最好。
所以能夠開一個 l a s t last 數組, l a s t [ i ] last[i] 標記要留守 i i 的最晚位置是哪裏。
如今開始思考留守的實質:若是在位置 i i 留守,就是使區間 [ l a s t [ i ] + 1 , n ] [last[i]+1,n] 的可用士兵數量減小 1 1
咱們要保證這段位置的可用士兵數量減小 1 1 以後還能佔領全部的城堡。
因而有:

inline bool check(const int &p)
{
    for(int i = p;i<=n;++i)
        if(a[i] + 1 > d[i])//d[i]表明佔領i前的可用士兵數量
            return false;
    return true;
}

隨後思考要怎麼選擇留守哪些城堡。能夠發現,貪心地留守權值大的城堡必定是最優的。
由於留守一個城堡影響的是 [ l a s t [ i ] + 1 , n ] [last[i]+1,n] 整個區間,咱們能夠討論:
若是 c [ i 1 ] > c [ i 2 ] c[i1] > c[i2] ,且 l a s t [ i 1 ] < l a s t [ i 2 ] last[i1] < last[i2] ,即 i 1 i1 的權值比 i 2 i2 大,有3種狀況:
1.[last[i1]+1,n]的區間所有減去2個士兵後依然能佔領全部城堡。那麼就是 i 1 i1 i 2 i2 都留守的狀況,此時操做順序不會影響結果。
2.[last[i1]+1,last[i2]]的區間減去1個士兵能知足,[last[i2]+1,n]減去2個士兵能知足,因爲咱們先處理權值大的,處理完 i 1 i1 後還能處理 i 2 i2 ,也能夠獲得正確答案。
3.[last[i1]+1,n]的區間減去1個士兵能知足,此時 i 1 i1 i 2 i2 只能選擇一個,那麼確定是選取 i 1 i1 好。
選取 i 1 i1 i 2 i2 的區別就在於 [ l a s t [ i 1 ] + 1 , l a s t [ i 2 ] ] [last[i1]+1,last[i2]] 這段區間,是否會對結果形成影響呢?
咱們將選擇 i 1 i1 看作消耗 [ l a s t [ i 1 ] + 1 , l a s t [ i 2 ] ] [last[i1]+1,last[i2]] [ l a s t [ i 2 ] + 1 , n ] [last[i2]+1,n] 內的士兵數量。
假定有一個 i 3 i3 的選擇,,且 c [ i 3 ] < c [ i 1 ] c[i3] < c[i1] ,若是 l a s t [ i 3 ] > l a s t [ i 2 ] last[i3] > last[i2] ,很顯然選擇 i 1 i1 i 2 i2 是等效的。
若是 l a s t [ i 3 ] < l a s t [ i 1 ] last[i3] < last[i1] ,那麼選取 i 3 i3 的代價比選取 i 1 i1 更大且得利更少,所以此時先選擇 i 1 i1 能夠保證總體更優。
若是 l a s t [ i 3 ] [ l a s t [ i 1 + 1 ] , l a s t [ i 2 ] ] last[i3]在[last[i1+1],last[i2]] 之間,若選 i 1 i1 後還能選 i 3 i3 則確定選 i 1 i1 ,若選 i 1 i1 後不能選 i 3 i3 ,那麼選 i 3 i3 後確定也不能選 i 1 i1 ,但 c [ i 3 ] < c [ i 1 ] c[i3] < c[i1] ,所以可能狀況下仍是選 i 1 i1 更好。
另外,若 c [ i 1 ] > c [ i 2 ] c[i1] > c[i2] l a s t [ i 1 ] > l a s t [ i 2 ] last[i1] > last[i2] ,很顯然此時選擇i1比選擇i2好。
可能寫的有點亂……

Code

#include <cstdio>
#include <algorithm>
template <typename T> inline T max(const T &a,const T &b){return a>b?a:b;}
inline char nc()
{
    static char buf[1000000],*p1 = buf,*p2 = buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
void read(int &r)
{
    static char c;r=0;
    for(c=nc();c>'9'||c<'0';c=nc());
    for(;c>='0'&&c<='9';r=(r<<1)+(r<<3)+(c^48),c=nc());
}
int n,m,k;
int last[5001];//last[i]保存i點最晚能在哪裏被派兵守衛
int a[5001],b[5001],c[5001],d[5001];//d[i]表明佔領i前到達i這裏的兵力
int q[5001];
bool cmp(const int &a,const int &b)
{
    return c[a] > c[b];
}
inline bool check(const int &p)
{
    for(int i = p;i<=n;++i)
        if(a[i] + 1 > d[i])
            return false;
    return true;
}
int main()
{
    read(n);
    read(m);
    read(k);
    int now = k;//如今兵力
    for(int i = 1;i<=n;++i)
    {
        read(a[i]);
        read(b[i]);
        read(c[i]);
        d[i] = now;
        if(now < a[i])
        {
            printf("-1");
            return 0;
        }
        now += b[i];//順便判斷是否能佔領全部城堡,也就是判斷每次取完兵可否一路所有佔領
        q[i] = last[i] = i;
    }
    int u,v;
    for(;m;--m)
    {
        read(u);
        read(v);
        last[v] = max(last[v],u);
    }
    std::sort(q+1,q+1+n,cmp);//按照貢獻值排序
    int ans = 0;
    for(int i = 1;i<=n;++i)
    {
        if(check(last[q[i]] + 1))
        {
            for(int j = last[q[i]] + 1;j<=n;++j)
                --d[j];
            ans += c[q[i]];
        }
    }
    printf("%d",ans);
    return 0;
}

這裏是反悔貪心的代碼:

#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
//讀入優化
inline char nc()
{
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
template <typename T>
inline void read(T &r)
{
    static char c;r = 0;
    for (c = nc(); c > '9' || c < '0'; c = nc());
    for (; c >= '0' && c <= '9'; r = (r << 1) + (r << 3) + (c ^ 48), c = nc());
}
const int maxn = 5e3 + 10;
int n, m, k, ans;
int a[maxn], b[maxn], c[maxn];
int last[maxn]; //last[i]記錄i最後能在哪裏被留守
priority_queue<int, vector<int>, greater<int>> q;
vector<int> defback[maxn]; //存儲該點能夠向前留守什麼點
int main()
{
    read(n);
    read(m);
    read(k);
    for (int i = 1; i <= n; ++i)
    {
        read(a[i]);
        read(b[i]);
        read(c[i]);
        last[i] = i; //初始化
    }
    int u, v;
    for (; m; --m)
    {
        read(u);
        read(v);
        last[v] = last[v] > u ? last[v] : u; //取更大者,由於越後留守必定越好
    }
    for (int i = 1; i <= n; ++i)
        defback[last[i]].push_back(i);
    for (int i = 1; i <= n; ++i)
    {
        while (k < a[i] && !q.empty()) //不夠的話,從已留守的城堡中取出權值最小的放棄,即反悔
            ++k, q.pop();
        //defended[q.top()] = false; 事實上並不須要對是否已經留守作標記,由於全部點最多會被判斷留守一次,也就是最多被標true false一次

        if (k < a[i]) //若是取完仍是不夠,那沒辦法了,輸出-1
            goto fail;

        //佔領當前點
        k += b[i];
        //往前留守,這裏不用考慮當前點,由於當前點要麼也在vector中,要麼在後面能夠被留守
        for (vector<int>::iterator it = defback[i].begin(); it != defback[i].end(); ++it)
            --k, q.push(c[*it]); //能夠直接所有push到留守集合中,等後面再反悔,不用考慮會不會讓k<0
    }
    //最後一步後還要判斷k是否大於0進行一次反悔
    while (k < 0 && !q.empty())
        ++k, q.pop();
    if (k < 0)
        goto fail;
    while (!q.empty())//統計已留守點的權值和
        ans += q.top(), q.pop();
    printf("%d", ans);
    return 0;
fail:
    puts("-1");
    return 0;
}
相關文章
相關標籤/搜索