AtCoder Beginner Contest 134 F.Permutation Oddness(dp)

这篇文章详细讲述了如何通过动态规划解决一个关于数列中元素匹配的题目,通过构建dp[i][j][k]来表示匹配策略,并逐步推导转移状态,最后给出了C++代码实例。理解了思路后,读者能掌握解决此类问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

思路来源

qls

题解

一道很久之前的题,看当时有人问,但自己没有补,现在才彻底想明白

将所求表达式考虑成这样一个匹配问题,

dp[i][j][k]表示当前考虑前i个数,还有j个左边的值要连向比它们大的右边的值(同时意味着还有j个右边的值要连向比它们大的左边的值),当前的绝对值之和是k的方案数

转移即加入第i种数,

此时分五种情况,以同时加入2左、2右为例,

注意到比和自己小的数匹配有j种方式,

①左右等,2左和2右匹配,dp[i][j][k+2*j]+=dp[i][j][k]

②左配右留,2左和右边已经出现的数匹配,2右留到以后再说,dp[i][j][k+2*j]+=dp[i][j][k]*j

③左留右配,2右和左边已经出现的数匹配,2左留到以后再说,dp[i][j][k+2*j]+=dp[i][j][k]*j

④左留右留,2左和2右都留到以后再说,dp[i][j+1][k+2*(j+1)]+=dp[i][j][k]

⑤左配右配,2左和右边已经出现的数匹配,2右和左边已经出现的数匹配,dp[i][j-1][k+2*(j-1)]+=dp[i][j][k]*j*j

注意到前三种可以合并,于是就分三种转移

tutorial死活看不懂,自己知道dp式的含义之后推一推就推出来了

代码

#include<bits/stdc++.h>
using namespace std;
const int N=51,mod=1e9+7;
int n,k,dp[N][N][N*N];
void add(int &x,int y){
	x=(x+y)%mod;
} 
int main(){
	scanf("%d%d",&n,&k);
	dp[0][0][0]=1;
	//dp[1][1][0]
	for(int i=0;i<n;++i){
		for(int j=0;j<=i;++j){
			for(int l=0;l<=k;++l){
				if(!dp[i][j][l])continue;
				if(l+2*j<=k)add(dp[i+1][j][l+2*j],1ll*(2*j+1)*dp[i][j][l]%mod);
				if(l+2*(j+1)<=k)add(dp[i+1][j+1][l+2*(j+1)],dp[i][j][l]);
				if(j-1>=0 && l+2*(j-1)<=k)add(dp[i+1][j-1][l+2*(j-1)],1ll*(j*j)*dp[i][j][l]%mod);
			}
		}
	}
	printf("%d\n",dp[n][0][k]);
	return 0;
}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值