一些dp题
(真的只有绿吗)
首先,很明显有,其中S代表初始魔力值,k为选择的物品数量,x为题目中给定的
然后注意到x很大,但k和n很小,所以我们可以设定状态,表示当前到第i个物品,已经选了j个,总和模k为l,这么选的初始魔法最大值(显然初始魔法越大时间花费越少),然后暴力枚举选的总数量并转移即可,时间复杂度
(oi的精髓在于枚举与模拟)
点击查看代码
void solve() {
cin>>n>>x;
for(int i=1;i<=n;i++) cin>>a[i];
for(int k=1;k<=n;k++) {
for(int i=0;i<=n+2;i++)
for(int j=0;j<=n+2;j++)
for(int l=0;l<=k;l++) dp[i][j][l]=0;
for(int i=1;i<=n;i++) {
dp[i][1][a[i]%k]=a[i];
for(int j=1;j<=n;j++)
for(int l=0;l<k;l++)
mx(dp[i][j][l],dp[i-1][j][l],dp[i-1][j-1][(l-a[i]%k+k)%k]==0?-inf:dp[i-1][j-1][(l-a[i]%k+k)%k]+a[i]);
} if(dp[n][k][x%k]) ans=min(ans,(x-dp[n][k][x%k])*1ll/k);
} printf("%lld",ans);
}
这个题花了很久才看明白怎么做,感觉题解写的很奇怪
首先,看到这个绝对值,我们很自然想到拆贡献,我们把第i位上填称为i与相匹配,那么一个数的贡献只与其匹配的数相关
具体地,先考虑每个i,如果与其匹配的数比它大,将绝对值拆开后i的贡献会变成-i,反之则是i,同理
所以单看每个数对奇怪度的贡献,只需要讨论它和匹配数的大小关系
我们把i和全放在一起考虑,可以设出一个状态表示当前考虑到第i位,前面有j个i和未匹配(它们未匹配的数量肯定相同),当前奇怪度为k
怎么转移呢?
首先,第i位上可以填i,此时贡献为0,有
然后,可以两个都与后面进行匹配,这时它们是比后面的数小,所以是正贡献
两个数也可以与前面的数匹配,此时是负贡献
也可以一个与前面匹配,一个与后面匹配,注意方案要乘2
最后注意奇怪度可以减成负数,我的处理是直接在k维上加了n*n
点击查看代码
void solve() {
cin>>n>>m; ll maxn=n*n*2; dp[0][0][n*n]=1;
for(int i=1;i<=n;i++) {
for(int j=0;j<=i;j++) {
for(int k=0;k<=maxn;k++) {
add(dp[i][j][k],dp[i-1][j][k]);
if(j) add(dp[i][j][k],dp[i-1][j][k]*2ll*j%M);
if(j&&k+i*2<=maxn) add(dp[i][j][k],dp[i-1][j-1][k+2*i]);
if(j<i-1&&k-i*2>=0) add(dp[i][j][k],dp[i-1][j+1][k-2*i]*(j+1)*1ll*(j+1)%M);
}
}
}
printf("%lld",dp[n][0][m+n*n]);
}