队测 逆序对 permut
本人水平有限,题解不到为处,请多多谅解
本蒟蒻谢谢大家观看
题目:
permut
题目描述:
求由 1 到 n 一共 n 个数字组成的所有排列中,逆序对个数为 k 的有多少个
输入格式:
第一行为一个整数 T,为数据组数。
以下 T 行,每行两个整数 n,k,意义如题目所述。
输出格式:
对每组数据输出答案对 10000 取模后的结果
Sample Input
1
4 1
Sample Output
3
数据范围及约定
对于 30% 的数据,满足 n≤12
对于所有数据,满足 n≤1000, k≤1000,T≤10
测试时全程开启O2优化
此题为逆序对:逆序对定义如下:
设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
首先看一下题面:由1~n组成的所有排列中,求逆序对个数等于k的排列数
我首先想到的是用全排列,不断去枚举用dfs去爆搜,(结果只拿了20分)解法如下:
#include<bits/stdc++.h> #pragma GCC optimize(3) #define mod 10000 using namespace std; int n,a[13],ans,cnt,t,k; int f[1001][20002]; bool b[13]; void inint(){ freopen("permut.in","r",stdin); freopen("permut.out","w",stdout); } void dfs(int num){ if(num==n+1){ cnt=0;//cnt每次做完一次排列后要清零,从重新统计 for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(a[i]>a[j]&&j>i)cnt++; } //cout<<a[i]<<" ";//" cnt= "<<cnt<<" "; } //cout<<endl; if(cnt==k) ans++; //cout<<endl; //cout<<ans<<endl; return ; } // cout<<ans<<endl; for(int i=1;i<=n;i++){ if(b[i]==false){ b[i]=true; a[num]=i; dfs(num+1); b[i]=0; a[num]=0; } } return ; } inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } int main() { //inint(); t=read(); while(t--){ ans=0; n=read(),k=read(); dfs(1); printf("%d\n",ans); } return 0; }
同理:ans一定要不断的重新清零。
显然解法1会TLE,当n==10是其已经不能胜任在1s内跑完。
这时,通过dfs我们可以输入几个数,发现可以使用DP(dfs在一定有规律时可以转化成DP)
解法2横空出世:设f[i][j]表示1~i的逆序对数为j的排列方案数。次设法刚好符合题意,我们就可以直接输出f[n][k]即可。
在任意一个1到i-1排列中插入i可能产生0,1,2……i-1个逆序对。
i-1
f[i][j]=∑f[i-1][j-k](j>=k)
k=0
因为有多组数据,所以枚举时n,k都要取max,来优化时间复杂度,其n,k都要用数组形式存储,防止数据更新。
code:
#include<bits/stdc++.h> #pragma GCC optimize(3) using namespace std; int f[5001][4951],n[11],k[11],t,maxn,maxk; inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1; ch=getchar(); } while(ch<='9'&&ch>='0'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int main() { t=read(); for(int i=1;i<=t;i++){ n[i]=read(),k[i]=read(); maxn=max(maxn,n[i]); maxk=max(maxk,k[i]); } f[0][0]=1; f[1][0]=1; f[2][0]=1; f[2][1]=1; for(int i=3;i<=maxn;i++){ for(int j=0;j<=maxk;j++){ for(int kk=0;kk<=i-1&&kk<=j;kk++){ f[i][j]=(f[i][j]+f[i-1][j-kk])%10000; } } } for(int i=1;i<=t;i++) printf("%d\n",f[n[i]][k[i]]); }