51nod 1250 排列与交换

1250 排列与交换
题目来源: HackerRank
基准时间限制:2 秒 空间限制:131072 KB 分值: 320 难度:7级算法题 收藏 关注
一个数组A = [1, 2, 3, …, n]。

对A进行好恰好k次相邻交换,能得到多少个不同的序列 (S1)?
对A进行最多k次交换,你能得到多少个不同的序列 (S2)?

一次相邻交换是指交换数组A中两个相邻位置的元素,即:交换A[i]和A[i+1]或者A[i]和A[i-1]。
一次交换是指交换数组A中的任意两个位置不同的元素,即:交换A[i]和A[j],1 <= i, j <= N, i != j。

给出数组A的长度N,以及次数K,求S1和S2。由于结果很大,输出Mod 1000000007的结果。
例如:原始数组: [1, 2, 3]

  1. 经过两次相邻交换:
    我们得到 [1, 2, 3], [2, 3, 1], [3, 1, 2] ==> S1 = 3

  2. 经过最多两次交换:
    1) 0次交换后: [1, 2, 3]
    2) 1次交换后: [2, 1, 3], [3, 2, 1], [1, 3, 2].
    3) 两次交换后: [1, 2, 3], [2, 3, 1], [3, 1, 2] ==> S2 = 6
    Input
    输入2个数N, K,中间用空格分隔,(1 <= N, K <= 3000)
    Output
    输出2个数S1, S2 Mod 1000000007的结果,中间用空格分隔。
    Input示例
    3 2
    Output示例
    3 6


【分析】

第一问逆序对DP。
第二问普通DP。


【代码】

//51nod 1250 排列与交换
#include<bits/stdc++.h>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=3005;
const int mod=1e9+7;
int n,k,ans;
int dp[mxn][mxn];
int main()
{
    int i,j;
    scanf("%d%d",&n,&k);
    dp[0][0]=1;
    fo(i,0,n)
    {
        if(i) fo(j,1,k) dp[i][j]=(dp[i][j]+dp[i][j-1])%mod;
        fo(j,0,k)
        {
            dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
            if(i+j+1<=k)
              dp[i+1][i+j+1]=(dp[i+1][i+j+1]-dp[i][j]+mod)%mod;
        }
    }
    fo(i,0,k) if((k-i)%2==0) ans=(ans+dp[n][i])%mod;
    printf("%d ",ans);
    ans=0,M(dp);
    fo(i,0,n) dp[i][0]=1;
    fo(i,1,n) fo(j,1,k)
      dp[i][j]=(dp[i-1][j]+(ll)(i-1)*dp[i-1][j-1]%mod)%mod;
    fo(i,0,k) ans=(ans+dp[n][i])%mod;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值