沈师PTA算法第二章作业

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 2k2k(k为正整数,k<=10,length= 2 k 2^k 2k)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格(其坐标为a,b,分别代表行坐标号和列坐标号),以及有四种L型骨牌(如下图)。求用若干块这种L型骨牌实现除该特殊点棋盘的全覆盖。(本题要求采用分治算法做)

棋盘.png

输入格式:

输入三个数,分别是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;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小飞睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值