挑选pick
题目在此继续省略(条件原因,有乱码)
那就概括一下题目大意:
n个小朋友,1~n从左到右排成一行,每个小朋友都有一个气场值ci和一个实力值wi.
假如丁爷爷选了第i个小朋友,就会将这个小朋友及他右边的ci个小朋友踢出。
现在要求能选到的最大的实力值和选的方案总数,方案总数模998244353.
这道题第一感觉就是dp,可是却看错了题目,最后转成暴力时间就不够了,最大实力值对了可没时间求方案数,真的是...丧心病狂啊!...那就讲满分算法吧,好打易懂。
由这个题意似乎可以往一种常见的dp去想那就是---括号序列!
这里我们显然可以转化成若第i个填左括号,那么就要有ci个右括号来与之匹配。
那不就非常简单了吗?!
f[i][j]表示前i个小朋友中左括号与右括号的差为j的最大实力值。
则f[i][j]=max{f[i-1][j+1](取右括号情况),f[i-1][j-c[i]]+w[i](取左括号情况)}
if j==0 f[i][j]=max{f[i][j],f[i-1][j]}可以不取。
辣么最后的结果就是f[n][0]啦~
初值f[0][0]=0就ok啦。
g[i][j]表示前i个小朋友中左括号与右括号的差为j的方案总数。
也是一样的道理,将max改成累加即可。
这道题的变形有点考思路,但变形好之后就很基本了qwq下次要记住啊...
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#define N 5050
#define ll long long
#define mod 998244353
using namespace std;
int n,now,w[N],c[N];
ll f[2][N],g[2][N];
int main()
{
freopen("pick.in","r",stdin);
freopen("pick.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
f[0][0]=0;now=0;
for(int i=1;i<=n;i++){
now=1-now;
for(int j=0;j<=n;j++){
if(!j){
f[now][j]=max(f[1-now][j],f[1-now][j+1]);
if(j>=c[i]) f[now][j]=max(f[now][j],f[1-now][j-c[i]]+w[i]);
}else{
f[now][j]=f[1-now][j+1];
if(j>=c[i]) f[now][j]=max(f[now][j],f[1-now][j-c[i]]+w[i]);
}
}
}
printf("%lld ",f[now][0]);
g[0][0]=1;now=0;
for(int i=1;i<=n;i++){
now=1-now;
for(int j=0;j<=n;j++){
if(!j){
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{
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;
}
}
}
printf("%lld\n",g[now][0]);
return 0;
}