这个题呢,一开始是用DP想的,但是没有按照DP的思路走,因为题目意思描述得很简单,显然是可以打表找规律的
先附上打表的程序
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
int dp[10][10],n;
int num[10];
bool vis[10];
void calc(){
//for(int i=1;i<=n;i++)
// printf("%d%c",num[i],i==n?'\n':' ');
int res=0;
for(int i=1;i<=n;i++)
if (num[i]>i) res++;
dp[n][res]++;
}
void dfs(int step){
if (step>n) calc();
for(int x=1;x<=n;x++)
if (!vis[x]){
num[step]=x;
vis[x]=true;
dfs(step+1);
vis[x]=false;
}
}
int main(){
n=7;
memset(dp,0,sizeof(dp));
memset(vis,false,sizeof(vis));
dfs(1);
for(int i=0;i<=n;i++)
printf("%d%c",dp[n][i],i==n?'\n':' ');
return 0;
}
代码就是用dfs生成了所有的排列,然后根据题意统计,dp【i】【j】:有i个数字,j个根据题意的匹配
我们可以修改n,n=3,4,5,6,7,8,……这些小数据打出来一个表,然后可以来找规律(这是一种可行的方法)
但是dp的方法呢,来递推公式:
dp【i】【j】会怎么来:
第一部分:dp【i-1】【j】,最大的第i个数放在最后面
第二部分:dp【i-1】【j】*j,最大的第i个数与前i-1个数字中的j个比原来位置上的数大的数互换位置,匹配值不变
第三部分:dp【i-1】【j-1】*(i-1-(j-1)),因为要增加一个匹配,就不能在原来已经有匹配的位置上选择
代码:
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define ll long long
const int maxn=1010;
ll dp[maxn][maxn];
const int mod=1e9+7;
int main(){
//freopen("input.txt","r",stdin);
memset(dp,0,sizeof(dp));
for(int i=1;i<=1000;i++){
dp[i][0]=1;
for(int j=1;j<=i;j++)
dp[i][j]=(dp[i-1][j]*(j+1)+dp[i-1][j-1]*(i-j))%mod;
}
int n,k;
while(scanf("%d%d",&n,&k)!=EOF)
printf("%lld\n",dp[n][k]);
return 0;
}