Codeforces Div.2 212 C Insertion Sort (DP)

本文介绍了一种结合插入排序与动态规划的算法题解法,通过动态规划预先计算数组中小于某一元素的数量,来减少交换两个元素后进行插入排序所需的swap调用次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

超级传送门: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值