1. 程式人生 > >兩道序列DP

兩道序列DP

(以下來自Luogu Voldermod 的10.21 Luogu個人賽的T1題面):

最後的戰役打響了。

哈利被宣告“死亡”,伏地魔帶著他的部下準備進攻霍格沃茨。但是霍格沃茨有古老的魔法保護,他們必須先摧毀這些保護。魔法保護一共有nnn層,每一層保護有兩個引數:k,p。其中k表示魔法的型別,p表示能量的大小。

伏地魔每秒都會穿過一層保護,他在第 i秒(到達了第 i 層)他有以下選擇:

1.收集 [1,i] 層魔法中魔法型別為 xii 的魔法能量

2.收集 [1,i]層中魔法能量最大那層的魔法能量

3.使用加倍魔法

對於上面三個選擇,他每秒可以可以選擇一個,並可能獲得能量,對於不同的選擇,獲得的能量也不同:

對於1.獲得[1,i]層中所有魔法型別為xi魔法能量

對於2.獲得[1,i]中魔法能量最大的那一層的魔法能量

對於3.這一秒總共收集的能量不變(也就是這一秒不收集新的能量),但是下一秒獲得的能量翻倍。但是他不能連續使用加倍魔法,而且他最多隻能使用m次,對於每一層的能量他都可以重複獲取

只有他通過了這n層保護,並獲得了最大的魔法能量才有可能徹底摧毀霍格沃茨的魔法防禦,可是巫師又是不擅長計算的。

於是,伏地魔找到了你,而你,作為精通計算機技術的麻瓜程式設計師,現在需要做的就是設計一個程式幫助伏地魔計算出他可以獲得的最大的魔法能量的值。

最終的決戰已經展開,魔法界的歷史又翻過了一頁……

 

顯然DP,容易得到狀態:設f[i][j][0/1]表示到了第i層,用了j次魔法,本次用(1)還是沒用(0)。

那麼狀態轉移方程就是:

res=max (k[PreM[i]], PreS[Map[x[i]]]); (Map是離散化用的,因為值域到了109

那麼f[i][j][0]=max (f[j][0]+res, f[j][1]+res*2), f[i-1][j][1]=f[i-1][j-1][0];

特別的 f[i][0][0]=f[i-1][0][0]+res。

但是注意到記憶體只有256MB,而f陣列要開long long,算一下有300多MB。空間炸了??

這個時候,注意到第i層只能有第i-1層轉移過來,所以滾掉一維,倒序列舉就OK了。

#include <bits/stdc++.h>
using namespace std;
inline int gi () {
    int x=0,w=0; char ch=0;
    while (!(ch>='0' && ch<='9')) {if (ch=='-') w=1; ch=getchar ();}
    while (ch>='0' && ch<='9') x= (x<<3)+(x<<1)+(ch^48), ch=getchar ();
    return w?-x:x;
}

#define LL long long
#define RG register

const int M=510;
const int N=5e4+10;

map <int,int> Map;
LL Ans,f[M][2];
int n,m,tot,ty[N],b[N],x[N],k[N],PreM[N],PreS[N];

int main ()
{
    RG int i,j,res;
    n=gi (), m=gi ();
    for (i=1;i<=n;++i) {
        ty[i]=gi (), k[i]=gi ();
        if (Map.find (ty[i])==Map.end ()) Map[ty[i]]=++tot;
    }
    for (i=1;i<=n;++i) x[i]=gi ();
    for (i=1;i<=n;++i) 
        if (k[i]>k[PreM[i-1]]) PreM[i]=i;
        else PreM[i]=PreM[i-1];
    //for (i=1;i<=n;++i) cout << ty[i] << ' ';
    //cout << '\n';
    //for (i=1;i<=n;++i) cout << PreM[i] << ' ';
    // cout << '\n';1
    f[0][0]=k[1], PreS[Map[ty[1]]]=k[1], Ans=k[1];
    for (i=2;i<=n;++i) {
        PreS[Map[ty[i]]]+=k[i];
        // cout << "PreSum:" << PreS[i] << '\n';
        res=max (k[PreM[i]], PreS[Map[x[i]]]);
        // cout << "res:" << res << '\n';
        for (j=min (m,i);j>=1;--j) {
            //    cout << "j=" << j << '\n';
            f[j][0]=max (f[j][0]+res, f[j][1]+res*2);
            //cout << f[j][0] << '\n';
            f[j][1]=f[j-1][0];
            //cout << f[j][1] << '\n';
            //cout << "now:" << Ans << '\n';
            Ans=max (Ans, max (f[j][0], f[j][1]));
            //cout << "now:" << Ans << '\n';
        }
        f[0][0]+=res;
        //cout << f[0][0] << '\n';
        Ans=max (Ans, f[0][0]);
    }
    printf ("%lld\n", Ans);
    return 0;
}

 

考場上還Debug了好一陣。哎。。