POJ 3761 Bubble Sort

本文介绍了冒泡排序的基本原理和过程,通过一个具体的例子展示了冒泡排序如何通过多次扫描和交换将序列排序。文章还提到了一个算法题目——POJ 3761,该题目要求找出已知通过K轮冒泡排序得到的升序序列的所有可能初始序列的数量,答案需模除20100713。

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

题目大意:

        (维基百科)冒泡排序时一种简单的排序算法,它重复对序列进行扫描并不停检查相邻量元素是否符合大小顺序,如果不符合就交换它们,直到不需要交换位置(便停止扫描,表示排序的终止),该算法因升序排列中每次扫描都将最小的元素交换至前面而得名(就如同泡泡从水底浮至水面一样),因为它只通过比较进行排序,因此它是一种比较排序法。

         该算法复杂度为O(n^2),假设经过T论扫描序列刚好称为升序序列(每一轮都有交换,无需交换时不用扫描),则称T为冒泡排序扫描数,例如:

           First Round:
            ( 5 1 4 2 8 ) -> ( 1 5 4 2 8 ), Compares the first two elements, and swaps them.
            ( 1 5 4 2 8 ) -> ( 1 4 5 2 8 ), Swap since 5 > 4
            ( 1 4 5 2 8 ) -> ( 1 4 2 5 8 ), Swap since 5 > 2
            ( 1 4 2 5 8 ) -> ( 1 4 2 5 8 ), since these elements are already in order (8 > 5), algorithm does not swap them.
           Second Round:
           ( 1 4 2 5 8 ) -> ( 1 4 2 5 8 )
           ( 1 4 2 5 8 ) -> ( 1 2 4 5 8 ), Swap since 4 > 2
           ( 1 2 4 5 8 ) -> ( 1 2 4 5 8 )
           ( 1 2 4 5 8 ) -> ( 1 2 4 5 8 )

           T = 2

           ZX在算法课上学习了冒泡排序,并且老师留给他回家作业,给定一已经升序排列的序列A(共有N个互不相同的元素),并且已经得知该序列是刚好通过K论冒泡排序得到的,现问可能的初始序列有多少种情况。

          现有多个测例(测例数量题中给出),每个测例中都会给出N和K(1 ≤ N ≤ 1,000,000,0 ≤ K ≤ N - 1),对于每个测例都给出可能的所有初始序列的总数,由于数据过大,将最后结果模除20100713。

题目链接

注释代码:

//关于模除的几个公式:
//(1) (a1 × a2 × a3....× an) mod k = (((...(((((a1 mod k) × a2) mod k) × a3) mod k)...an-1) mod k) × an) mod k
//(2) (a - b) mod k = ( ((a mod k) + k) - (b mod k) ) mod k
//(3) (a × b) mod k = ( (a mod k) × (b mod k) ) mod k

//此题中刚好扫描k次就等价于数列中元素的逆序数最大为k
//并且第i号元素的逆序数为[0, i - 1]
//因此根据排列组合知识可得总的初始状态可能为:
//( k! × ((k + 1)^(n - k) - k^(n - k)) ) mod 20100713
//k! mod MOD由(1)算出
//(a^b) mod MOD由(2)在配合二分法算出(mulmod函数)
//k! mod MOD的结果和(a^b) mod MOD的结果由(3)合并
//最后的减法部分(a - b) mod MOD由(2)算出,最终得到答案

/*                         
 * Problem ID : POJ 3761 Bubble Sort 
 * Author     : Lirx.t.Una                         
 * Language   : C             
 * Run Time   : 469 ms                         
 * Run Memory : 8000 KB                         
*/ 

#include <stdio.h>

#define	MOD		20100713LL

//maximum k
#define	MAXK	1000000

typedef	long long	dlong;

dlong	fac[MAXK];//factorial阶乘,fac[i]存放i! mod MOD的结果

dlong
mulmod( dlong a, dlong n ) {//multiply mod
	//计算乘方取模,a为底数,n为指数
	
	dlong	ans;
	
	ans = 1;
	while ( n ) {
		
		if ( n & 1LL ) {//二分法,当n为奇时乘模一次
			
			ans *= a;
			ans %= MOD;
		}
		
		//剩下的情况或者是奇数处理过一次后就都是偶数的形式了
		a *= a;//底数平方一次
		a %= MOD;//再取模,这样复杂度降为log(N)
		
		n >>= 1LL;//注意,这里需要二分,即除以2
	}
	
	return ans;
}

int
main() {
	
	int		t;
	dlong	n, k;
	dlong	ans1, ans2, ans;
	int		i;
	
	fac[0] = 1;
	fac[1] = 1;
	for ( i = 2; i < MAXK; i++ )
		fac[i] = fac[i - 1] * i % MOD;
	
	scanf("%d", &t);
	while ( t-- ) {
		
		scanf("%lld%lld", &n, &k);
		
		if ( !k ) {
			
			puts("1");
			continue;
		}
		
		//(1)
		ans1 = fac[k];
		ans2 = fac[k];
		
		ans1 *= mulmod( k + 1, n - k );//(1) + 二分
		ans1 %= MOD;//(3)
		
		ans2 *= mulmod( k, n - k );//(1) + 二分
		ans2 %= MOD;//(3)
		
		//(2)
		ans1 += MOD;
		ans  =  ( ans1 - ans2 ) % MOD;
		
		printf("%lld\n", ans);
	}
	
	return 0;
}

无注释代码:

#include <stdio.h>

#define	MOD		20100713LL

#define	MAXK	1000000

typedef	long long	dlong;

dlong	fac[MAXK];

dlong
mulmod( dlong a, dlong n ) {
	
	dlong	ans;
	
	ans = 1;
	while ( n ) {
		
		if ( n & 1LL ) {
			
			ans *= a;
			ans %= MOD;
		}
		
		a *= a;
		a %= MOD;
		
		n >>= 1LL;
	}
	
	return ans;
}

int
main() {
	
	int		t;
	dlong	n, k;
	dlong	ans1, ans2, ans;
	int		i;
	
	fac[0] = 1;
	fac[1] = 1;
	for ( i = 2; i < MAXK; i++ )
		fac[i] = fac[i - 1] * i % MOD;
	
	scanf("%d", &t);
	while ( t-- ) {
		
		scanf("%lld%lld", &n, &k);
		
		if ( !k ) {
			
			puts("1");
			continue;
		}
		
		ans1 = fac[k];
		ans2 = fac[k];
		
		ans1 *= mulmod( k + 1, n - k );
		ans1 %= MOD;
		
		ans2 *= mulmod( k, n - k );
		ans2 %= MOD;
		
		ans1 += MOD;
		ans  =  ( ans1 - ans2 ) % MOD;
		
		printf("%lld\n", ans);
	}
	
	return 0;
}

单词解释:

bubble:n, 气泡,泡沫

sorting:n, 排序

step through:vt, 单步调试,逐句通过

repeat:vt, 重复,背诵

repeatedly:adv, 重复地,再三地

adjacent:adj, 邻接的,毗连的

sorted:adj, 排好序的,分好类的

comparison:n, 比较,对照

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值