題意:
逆序對指在一個序列中ai>aj && i < j,也就是一前一後兩個數,當大的在前面的時候即算一對。c++
題目求在一個由1…n組成的序列中逆序對爲k的序列的個數。
出題人很良心,不須要寫高精度,答案對10000取模便可。算法
思路:
這道題的前面其實還有一道很相似的題,也是求逆序對。不一樣的是那道題給定了序列求逆序對的個數,而這道題則相反。那道題的方法即merge sort。儘管兩題很相似,可是方法卻大相徑庭。數組
憑感受,這應該是一道動規的題。長度爲n-1的序列與長度爲n的序列只差一個n,而因爲n出現的逆序對也很好求:n在第i個就會相比於原序列多出n-i個逆序對。如此,那看來是動規無疑了。markdown
狀態轉移方程:若長度爲n的序列要求有k個逆序對,那麼他能夠從長爲n-1的序列中選取有k-n+1到k個逆序對的部分。道理很簡單n的加入最多使原序列增長n-1個逆序對(即放在第一個的時候,與後面n-1個數構成逆序對),因此要從x[n-I][p]變到x[n][k],p屬於[k-n+1,k]。那麼轉移方程式就很好寫了:優化
for(unsigned i = 2; i != n+1; ++i)
{
dp[i][0] = 1;
for(unsigned j = 1; j != k+1; ++j)
{
for(unsigned p = k-j+1; p != j+1; ++p)
{
dp[i][j] += dp[i-1][p];
}
}
}
優化:
這是一個O(n^3)的算法,很慢。咱們觀察最內層循環,發現它不少餘,由於老是重複操做。dp[i][j-1]和dp[i][j]當i-j+1>0的時候,中間部分徹底相同,只是掐頭增尾的問題。由此咱們能夠有第一種優化:dp[i][j] = dp[i][j-1]-dp[i-1][j-i]+dp[i-1][j];(j-i >-1)spa
固然也能夠直接採用前綴數組和的方式將這段循環優化掉,到O(n^2)。
(後面的代碼採起的是這一種)debug
源代碼的讀入是多組數據。code
源代碼:regexp
#include <bits/stdc++.h>
#define maxn 1005
#define mod 10000
//#define DEBUG
#ifdef DEBUG
#define debug(...)printf(__VA_ARGS__);
#else
#define debug(...)
#endif
using namespace std;
int n[15], k[15], dp[maxn][maxn*10];
void Dp(int n, int k)
{
int a;
memset(dp, 0, sizeof dp);
for(unsigned i = 0; i != k+1; ++i)
{
dp[1][i] = 1;
}
for(int i = 2; i != n+1; ++i)
{
dp[i][0] = 1;
for(int j = 1; j != k+1; ++j)
{
if(j-i > -1) a = dp[i-1][j-i];
else a = 0;
dp[i][j] = (dp[i][j-1]+dp[i-1][j]-a+mod)%mod;
debug("%d ", dp[i][j]);
}
debug("\n");
}
return ;
}
int main()
{
//freopen("test.in", "r", stdin);
int times, Maxn = 0, Maxk = 0;
scanf("%d", ×);
for(unsigned i = 0; i != times; ++i)
{
scanf("%d%d", &n[i], &k[i]);
Maxn = max(Maxn, n[i]);
Maxk = max(Maxk, k[i]);
}
Dp(Maxn, Maxk);
for(unsigned i = 0; i != times; ++i)
{
printf("%d\n", (mod+dp[n[i][k[i]]-dp[n[i]][k[i]-1])%mod);
}
return 0;
}
箜瑟_qi 2017.04.27 15:01string