超级传送门:http://codeforces.com/contest/362/problem/C
这是一道以插入排序(Insertion Sort)为基础的动态规划题目,不熟悉插入排序请参考维基百科插入排序
题目中给的插入排序代码如下所示:
for (int i = 1; i < n; i = i + 1)
{
int j = i;
while (j > 0 && a[j] < a[j - 1])
{
swap(a[j], a[j - 1]); // swap elements a[j] and a[j - 1]
j = j - 1;
}
}
略去吐槽若干字。
题目输入一个0~n-1的无序排列,要求我们交换其某两个元素之后,再将新的无序排列采用上述插入排序时swap的调用次数最少,并求出可以达到同样效果(同样最少调用次数)的初始交换元素对的个数。
比如:
4 0 3 1 2
原本需要6次swap调用。
交换4和1后,得到:
1 0 3 4 2
只需要3次swap调用。
同样交换4和2也可以产生3次swap调用的序列。
所以得到答案(最少需要3次swap调用,有2种初始交换方案):
3 2
设原数组为a,设dp[i][j]为a[0]~a[i]中小于j的个数,则:
dp[0][j] = 0 (0 <= j < n),
dp[i][j] = dp[i-1][j] + (a[i] < j ? 1 : 0)
原序列所需要的swap调用次数old = Sigma(i - dp[i][a[i]]) (1 <= i < n)
若交换a[i]和a[j]( a[i] > a[j]时交换才有意义),我们作如下分析:1. a[i]之前的元素(a[0] ~ a[i-1])的swap调用次数不会变;
2. a[j]之后的元素(a[j+1] ~ a[n-1])的swap调用次数不会变;
3. a[i]和a[j]之间的元素(a[i+1] ~ a[j-1])的swap调用次数会发生改变,变化量为-p+q(p为a[i+1]~a[j-1]中小于a[i]的元素个数,q为a[i+1]~a[j-1]中小于a[j]的元素个数)
4. a[i]处的swap调用次数会发生改变(从i位置挪到j位置),变化量为(j-i+1)-p
5. a[j]处的swap调用次数会发生改变(从j位置挪到i位置),变化量为-((j-i+1)-q)-1
故new = old-p+q+(j-i+1)-p-((j-i+1)-q)-1 = old-2*(p-q)-1
其中p = dp[j - 1][a[i]] - dp[i][a[i]], q = dp[j - 1][a[j]] - dp[i][a[j]]
代码如下:
#include <stdio.h>
using namespace std;
int dp[5005][5005];
int a[5005];
int main()
{
int n;
while (scanf("%d", &n) > 0 && n)
{
int old = 0;
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
for (int j = 0; j < n; j++)
{
dp[0][j] = a[0] < j ? 1 : 0;
}
for (int i = 1; i < n; i++)
{
for (int j = 0; j < n; j++)
{
dp[i][j] = dp[i - 1][j] + (a[i] < j ? 1 : 0);
}
old += i - dp[i][a[i]];
}
int min = old;
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (a[i] > a[j])
{
int p = dp[j - 1][a[i]] - dp[i][a[i]];
int q = dp[j - 1][a[j]] - dp[i][a[j]];
int tmp = old - 2 * (p - q) - 1;
min = min < tmp ? min : tmp;
}
}
}
int ans = 0;
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (a[i] > a[j])
{
int p = dp[j - 1][a[i]] - dp[i][a[i]];
int q = dp[j - 1][a[j]] - dp[i][a[j]];
int tmp = old - 2 * (p - q) - 1;
if (tmp == min)
{
ans++;
}
}
}
}
printf("%d %d\n", min, ans);
}
return 0;
}