挑选pick

来源:互联网 发布:pc网络电视直播软件 编辑:程序博客网 时间:2024/04/30 04:03
挑选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 998244353using 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;}


原创粉丝点击