7-1 逆序数 (20 分)
设 x 1 , x 2 , x 3 . . . , x n x_1,x_2,x_3...,x_n x1,x2,x3...,xn是集合{1,2,3,…,n}的一个排列,排列中逆序对的对数称为逆序数,(如1432的逆序数为3,即有3对逆序对,分别为:43,42,32)。则当 x 3 x_3 x3=4时(即第3个数为4),所有排列的逆序数的和为多少?(n=6时,为2020年全国高中数学联赛浙江赛区初赛试题填空第10题)
附2020年全国高中数学联赛浙江赛区初赛试题填空第10题
设 i 1 , i 2 , … , i n i_1,i_2,…,i_n i1,i2,…,in是集合{1,2,…,n}的一个排列,如果存在k<l,且 i k i_k ik> i l i_l il,则称数对( i k i_k ik, i l i_l il)为一个逆序,排列中多有逆序数对的数目称为此排列的逆序数。比如,排列1432的逆序为43,42,32,此排列的逆序数就是3.则当n=6时,且 i 3 i_3 i3=4,的所有排列的逆序数的和为________________________________。来源:http://blog.sina.com.cn/s/blog_5618e6650102zpno.html
输入格式:
输入一个n(n<10)。
输出格式:
输出逆序数的和。
输入样例:
6
输出样例:
912
C++法:
//#include<bits/stdc++.h> //万能头文件
#include <cstring>
#include "iostream"
using namespace std;
int n, x[100], vis[100], sum = 0;
void dfs(int t){
if(t > n){
if(x[3] == 4){
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
if(x[i] > x[j]) sum++;
}
}
}
}
else{
for(int i = 1; i <= n; i++){
if(!vis[i]){
vis[i] = 1;
x[t] = i;
dfs(t + 1);
vis[i] = 0;
}
}
}
}
void init(){
memset(vis, 0, sizeof(vis));
}
int main(){
init();
cin >> n;
dfs(1);
cout << sum << endl;
return 0;
}
Java写法:(输入测试用例6之后,返回的值为0不知道为什么,欢迎各位大佬指点一二)
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int n=0;
static int sum = 0;
static int []x = new int[100];
static int []vis = new int[100];
static void dfs(int t){
if(t > n){
if(x[3] == 4){
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
if(x[i] > x[j]) {
sum++;
}
}
}
}
}
else{
for(int i = 1; i <= n; i++){
if (vis[i]!=0) {
continue;
}
vis[i] = 1;
x[t] = i;
dfs(t + 1);
vis[i] = 0;
}
}
}
static void init(){
Arrays.fill(vis,-1);
}
public static void main(String[] args) {
init();
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
dfs(1);
System.out.println(sum);
}
}
7-2 找第k小的数 (20 分)
设计一个平均时间为O(n)的算法,在n(1<=n<=1000)个无序的整数中找出第k小的数。
提示:函数int partition(int a[],int left,int right)的功能是根据a[left]–a[right]中的某个元素x(如a[left])对a[left]–a[right]进行划分,划分后的x所在位置的左段全小于等于x,右段全大于等于x,同时利用x所在的位置还可以计算出x是这批数据按升非降序排列的第几个数。因此可以编制int find(int a[],int left,int right,int k)函数,通过调用partition函数获得划分点,判断划分点是否第k小,若不是,递归调用find函数继续在左段或右段查找。
输入格式:
输入有两行:
第一行是n和k,0<k<=n<=10000
第二行是n个整数
输出格式:
输出第k小的数
输入样例:
在这里给出一组输入。例如:
10 4
2 8 9 0 1 3 6 7 8 2
输出样例:
在这里给出相应的输出。例如:
2
#include <iostream>
using namespace std;
//利用数组的第一个元素将数组划分为两部分,前面都比第一个元素小,
//后面都比第一个元素大,与快速排序划分完全相同
int partition(int a[], int left, int right) {
int i = left + 1;
int j = right;
int x = a[left];
while(true) {
while (a[i] < x &&i< right) i++;
while (a[j] > x) j--;
if (i>= j) break;
swap(a[i], a[j]);
}
swap(a[left], a[j]);
return j;
}
//在下标从left到right的数组元素中输出第K小的数
int find(int num[], int left, int right, int k) {
int p = partition(num, left, right); //返回划分元素的下标
int n = p - left + 1; // 划分元素为第n小
int t;
if ( n == k)
t = num[p];
else if (n < k) //找右半部分的第k-n小
t = find(num, p + 1, right, k - n);
else //找左半部分的第k小
t = find(num, left, p - 1, k);
return t;
}
int main() {
int n, k;
cin >> n >> k;
int num[n];
for (int i = 0; i< n; i++)
cin>>num[i];
cout<< find(num, 0, n - 1, k) <<endl;
return 0;
}
7-3 二分查找 (20 分)
输入n值(1<=n<=1000)、n个非降序排列的整数以及要查找的数x,使用二分查找算法查找x,输出x所在的下标(0~n-1)及比较次数。若x不存在,输出-1和比较次数。
输入格式:
输入共三行: 第一行是n值; 第二行是n个整数; 第三行是x值。
输出格式:
输出x所在的下标(0~n-1)及比较次数。若x不存在,输出-1和比较次数。
输入样例:
4
1 2 3 4
1
输出样例:
0
2
#include<iostream>
using namespace std;
extern int sum=0;
int BinarySearch(int a[],const int &x,int n)
{
int left = 0;
int right = n-1;
while (left <= right)
{
sum++;
int middle = (left + right)/2;
if(x == a[middle]) return middle;
if(x > a[middle]) left = middle + 1;
else right = middle - 1;
}
return -1;
}
int main()
{
int n,a[1000],x,result;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
cin>>x;
result = BinarySearch(a,x,n);
cout<<result<<endl<<sum;
return 0;
}
7-4 棋盘覆盖 (20 分)
在一个 2 k ∗ 2 k 2^k∗2^k 2k∗2k(k为正整数,k<=10,length= 2 k 2^k 2k)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格(其坐标为a,b,分别代表行坐标号和列坐标号),以及有四种L型骨牌(如下图)。求用若干块这种L型骨牌实现除该特殊点棋盘的全覆盖。(本题要求采用分治算法做)
输入格式:
输入三个数,分别是a,b,length.
输出格式:
输出整个棋盘。其中特殊方格填为0,然后铺棋盘的顺序为:先铺四个子棋盘交界的部分,然后递归的对每个子棋盘按照左上,右上,右下,左下的顺时针顺序铺满棋盘。每一块骨牌中三个方格数字相同,按照顺序标号,即第一块骨牌全标为1,第二块骨牌全标为2,…,以此类推。输出的每个数占4个场宽,右对齐。
输入样例:
1 1 4
表示:特殊格子为(1,1),棋盘有4行4列。
输出样例:
0 2 3 3
2 2 1 3
5 1 1 4
5 5 4 4
提示:先铺三个1,再铺三个2,…,最后铺三个5(即先处理子问题交界的地方,再处理左上,右上,右下,左下的子问题).
#include<iostream>
using namespace std;
#define N 90
int board[N][N];
int tile=1;
void chessboard(int tr,int tc,int dr,int dc,int size)
{
if(size==1)
return;
int t=tile++;
int s=size/2;
if(dr<tr+s&&dc<tc+s)
chessboard(tr,tc,dr,dc,s);
else
{
board[tr+s-1][tc+s-1]=t;
chessboard(tr,tc,tr+s-1,tc+s-1,s);
}
if(dr<tr+s&&dc>=tc+s)
chessboard(tr,tc+s,dr,dc,s);
else
{
board[tr+s-1][tc+s]=t;
chessboard(tr,tc+s,tr+s-1,tc+s,s);
}
if(dr>=tr+s&&dc>=tc+s)
chessboard(tr+s,tc+s,dr,dc,s);
else
{
board[tr+s][tc+s]=t;
chessboard(tr+s,tc+s,tr+s,tc+s,s);
} if(dr>=tr+s&&dc<tc+s)
chessboard(tr+s,tc,dr,dc,s);
else
{
board[tr+s][tc+s-1]=t;
chessboard(tr+s,tc,tr+s,tc+s-1,s);
}
}
int main()
{
int aa,bb,length;
cin>>aa>>bb>>length;
chessboard(1,1,aa,bb,length+1);
int i,j;
for(i=1;i<=length;i++)
{
for(j=1;j<=length;j++)
printf("%4d",board[i][j]);
cout<<endl;
}
}
7-5 循环日程表 (20 分)
设有N名选手进行循环比赛,其中N= 2 M 2^M 2M,要求每名选手要与其他N−1名选手都赛一次,每名选手每天比赛一次,循环赛共进行N−1天,要求每天没有选手轮空。
输入格式:
输入:一个整数M(M<=7)。
输出格式:
输出:表格形式的比赛安排表。一行各数据间用一个空格隔开。
输入样例:
3
输出样例:
在这里给出相应的输出。例如:
1 2 3 4 5 6 7 8
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1
说明,第一行为:1 2 3 4 5 6 7 8,1表示本行都是1号选手和其他选手的比赛,如第2个数为2(其下标可以看成1)表示第一天1号和2号比赛,第5个数为5(其下标可以看成4),表示1号和5号在第4天比赛。
#include<iostream>
#include<cmath>
using namespace std;
int a[1000][1000];
void f(int k,int n)
{
if(n==2)
{
a[k][1]=k;
a[k][2]=k+1;
a[k+1][1]=k+1;
a[k+1][2]=k;
}
else
{
f(k,n/2);
f(k+n/2,n/2);
for(int i=k;i<k+n/2;i++)
for(int j=n/2+1;j<=n;j++)
a[i][j]=a[i+n/2][j-n/2];
for(int i=k+n/2;i<k+n;i++)
for(int j=n/2+1;j<=n;j++)
a[i][j]=a[i-n/2][j-n/2];
}
}
int main()
{
int n,m;
cin>>m;
n=pow(2,m);
f(1,n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
cout<<a[i][j]<<' ';
cout<<endl;
}
return 0;
}