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下次要記住啊...
#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; }