挑選(pick)

挑選(pick)

1s/128MBspa

【題目背景】code

NOIP2017 立刻就要到了,丁爺爺想要從他的小朋友裏挑選出一些厲害的來參加NOIP。ci

 【題目描述】get

丁爺爺共有 n 個小朋友,按編號 1 . . . n 從左到右排成一行。每一個小朋友都有一個實力值,第 i 個小朋友的實力值爲 wi。it

丁爺爺做爲一名爺爺,天然能夠隨意地挑選小朋友。可是若是他挑選的小朋友太靠  近,就免不了給人一種內定、欽點的感受了。他並不想這麼作,因而他制定出了一個  方案。io

他給每一個小朋友設置了一個氣場值 ci,當丁爺爺挑選出 i 號小朋友參加 NOIP 後, 他會把它右邊的ci個小朋友(不包被挑選的小朋友自己)都自動淘汰。這些被淘汰的小朋友加上被丁爺爺挑選出的小朋友當即被移出隊伍。接着,丁爺爺就能夠繼續他的挑選工做。須要注意的是,若是此時右邊的小朋友個數不足ci,那麼丁爺爺是不能進行此次挑選的。數據

做爲丁爺爺的粉絲,你和 Yazid 都對丁爺爺的挑選工做很感興趣。di

你想知道丁爺爺小朋友們最高的實力值總和,而 Yazid則對挑選小朋友的本質不一樣的方案數很感興趣。(兩種挑選方案本質相同,當且僅當兩種方案最終挑選出的小朋友徹底一致。)文件

衆所周知,Yazid 是一名辣雞蒟蒻,因而你就須要同時求解這兩個問題。因爲方案數可能很大,因此對於 Yazid 的問題,你只須要回答對 998244353 取模的結果便可。while

 【輸入格式】

從文件 pick.in 中讀入數據。

第一行一個正整數 n,表示小朋友的數目。

接下來一行 n 個用空格隔開的非負整數 w1 . . . wn,依次描述 1 號小朋友至n號小朋友的實力值。

接下來一行 n 個用空格隔開的非負整數 c1 . . . cn,依次描述 1 號小朋友至n號小朋友的氣場值。

 【輸出格式】

輸出到文件 pick.out 中。

輸出一行 2 個用空格隔開的整數,第一個整數表示挑選出的小朋友實力值總和的最大值,第二個整數表示本質不一樣的挑選方案數對 998244353 取模的結果。

【樣例 1 輸入】

3

7 2 3

2 0 0

【樣例 1 輸出】

7 5

【樣例 2 輸入】

18

27 68 75 76 75 75 68 40 33 62 58 88 4 75 766 84 52

1 2 3 3 2 4 1 2 3 3 5 1 3 1 1 3 2 1

【樣例 2 輸出】

490 3348

【樣例 3】

見選手目錄下的 pick/pick3.in 與 pick/pick3.ans。

 【提示】

樣例 1 解釋:你能夠挑選出 {}, {1}, {2},{3},{2, 3} 這些小朋友的子集,所以方案數爲

5。其中,{1} 這個子集的小朋友實力值總和最大,總和爲 7(只有 1 號小朋友)。

 【子任務】

對於10% 的數據,保證 n ≤ 9。對於 30% 的數據,保證 n ≤ 18。對於 60% 的數據,保證 n ≤ 200。

對於另外20% 的數據,保證全部的 wi= 1,保證全部的 ci= 1。對於 95% 的數據,保證n≤ 3, 000。

對於 100% 的數據,保證 n ≤ 5, 000,wi ≤ 10^7,ci <n。

由這個題意彷佛能夠往一種常見的dp去想那就是---括號序列!
這裏咱們顯然能夠轉化成若第i個填左括號,那麼就要有ci個右括號來與之匹配。
那不就很是簡單了嗎?!
dp[i][j]表示前i個小朋友中左括號與右括號的差爲j的最大實力值。
則dp[i][j]=max{dp[i-1][j+1](取右括號狀況),dp[i-1][j-c[i]]+wi}
if j==0 dp[i][j]=max{dp[i][j],dp[i-1][j]}能夠不取。
辣麼最後的結果就是dp[n][0]啦~
初值dp[0][0]=0就ok啦。
g[i][j]表示前i個小朋友中左括號與右括號的差爲j的方案總數。
也是同樣的道理,將max改爲累加便可。
這道題的變形有點考思路,但變形好以後就很基本了qwq下次要記住啊...

code

#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int mod = 998244353;
const int MAXN = 5005;

inline ll read() {
    ll k = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        k = (k << 1) + (k << 3) + (ch & 15);
        ch = getchar();
    }
    if (f == -1) k = ~k + 1;
    return k;
}

inline void write(ll x) {
    if (x < 0)
        x = -x, putchar('-');
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

int n;
int now;
ll w[MAXN],c[MAXN];
ll dp[2][MAXN],g[2][MAXN];
int main(){
    freopen("pick.in","r",stdin);
    freopen("pick.out","w",stdout);
    //n = read();
    scanf("%d",&n);

    for (int i = 1 ; i <= n ; i++){
        //w[i] = read();
        scanf("%d",&w[i]);
    }   
    for (int i = 1 ; i <= n ; i++){
        //c[i] = read();
        scanf("%d",&c[i]);
    }   

    g[0][0] = 1;

    for (int i = 1 ; i <= n ; i++){
        now = 1 - now;
        for (int j = 0 ; j <= n ; j++){
            if (j == 0){

                dp[now][j] = max(dp[1 - now][j],dp[1 - now][j + 1]);
                if(j >= c[i]) dp[now][j] = max(dp[now][j],dp[1 - now][j - c[i]]+  w[i]);

                g[now][j] = (g[1 - now][j] + g[1 - now][j + 1]) % mod;
                if(j >= c[i]) g[now][j] = (g[now][j] + g[1 - now][j - c[i]]) % mod;

            }

            else{

                dp[now][j] = dp[1 - now][j + 1];
                if(j >= c[i]) dp[now][j] = max(dp[now][j],dp[1 - now][j - c[i]] + w[i]);

                g[now][j] = g[1 - now][j + 1];
                if(j >= c[i]) g[now][j] = (g[now][j] + g[1 - now][j - c[i]]) % mod;
            }
        }
    }

    //write(dp[now][0]),putchar(32),write(g[now][0]);
    printf("%lld %lld",dp[now][0],g[now][0]);
    return 0;
}
相關文章
相關標籤/搜索