现在的位置: 首页 > 综合 > 正文

【Vijos】P1757 – 逆序对

2018年05月02日 ⁄ 综合 ⁄ 共 1698字 ⁄ 字号 评论关闭

http://user.qzone.qq.com/1304445713/blog/1351823901

【Vijos】P1757 - 逆序对
这是一道递推题,可以通过暴力程序找规律(但是这个规律并不好找)。

现在我们来分析这个问题:

用 f(n, k) 表示 n 的全排列 的 k逆序对 的排列种数。并且对于不存在的排列,我们都设为0。

可知,n为任何值时,f(n, 0) = 1。那么,f(n, k) 是多少呢,我们来假设全排列的第一位。
当第一位是1时,因为 1 比任何数小,所以不会与后面的数构成逆序对,也就是说,
后面的 (n-1) 个数还要构成 k 个逆序对,所以数量是 f(n-1, k)。
当第一位是 2 时,会与后面的 1 构成一对逆序对,也就是说,
后面还要构成 k - 1 个逆序对,也就是 f(n-1, k-1)。
当第一位是 3 时,同理,3和后面的数构成 2 个逆序对,所以数量是 f(n-1, k-2)。

递推公式似乎得到了,但是,还要注意边界!
因为是 n 个数的全排,所以第一位不会大于 n ,那么最后一个被加上的数只是 f(n-1, k-n+1),而不是 f(n-1,0)。

好了,至此,递推公式得出 f(n, k) = sigma{ f(n-1, i) }  i = (k - n + 1) to (k - 1)  //sigma是求和累加

还没结束!这个递推公式中含有一个循环,效率太低,我们还可以继续推导!

如果用了暴力程序打表,我们会发现一个规律,很多数据 f(n, k) = f(n-1, k) + f(n, k-1)
这是为什么呢? 我们来分析 f(n, k-1) 是个什么东西:
根据我们已经得出的递推公式,可以知道,f(n, k-1) = sigma{ f(n-1, i) }  i = (k - n) to (k - 2)
发现了!这个式子中,包含了我们求 f(n, k) 所需的大量元素,只是多了一个 f(n-1, k-n)!

所以,递推公式被化简,得到 f(n, k) = f(n-1, k) + f(n, k-1) - f(n-1, k-n)
此题的复杂度被降低为 O(n2),可以秒杀了~

此题还有几个关于取模的细节需要注意:
首先是求余定理, (a + b) % m = ((a % m) + (b % m)) % m
所以此题直接在推导过程中每次取模就行。
但是需要注意的是,因为取过模,f(n-1, k) + f(n, k-1) 可能比 f(n-1, k-n) 更小!
所以要判断,出现负数时,把模加回去!

代码如下:(如需复制,用UltraEdit等编辑器,alt+选择前三列,可以删除行号)

01 #include <cstdio>
02 
03 using namespace std;
04 
05 int t, n, k;
06 int ans[1005][1005] = {}; 
07 
08 void pro();
09 
10 int main()
11 {
12     scanf("%d", &t);
13     
14     pro();
15     //预先求出整个答案表 
16     
17     while (t--)
18     {
19         scanf("%d%d", &n, &k);
20         printf("%d\n", ans[n][k]);
21     }
22     
23     return 0;
24 }
25 
26 void pro()
27 {
28     for (int i = 1i <= 1000i++)
29         ans[i][0] = 1;
30 
31     for (int i = 2i <= 1000i++)
32         for (int j = 1j <= 1000j++)
33         {
34             ans[i][j] = (ans[i-1][j] + ans[i][j-1]) % 10000;
35             if (j >= i//P党可以把数组下标开负数,C党就老老实实判断吧
36             {
37                 ans[i][j] -= ans[i-1][j-i];
38                 if (ans[i][j] < 0)
39                     ans[i][j] += 10000;
40             }
41         }
42 }

抱歉!评论已关闭.