ICPC训练联盟2021寒假冬令营(4)_2021.01.21_笔记

试题链接

点我进入代码提交OJ

学习笔记-排序算法( O(n2)时间复杂度 )

选择排序程序段(C++)

void selection_sort(int arr[], int len)
{
int i, j;
	for (i = 0 ; i < len - 1 ; i++)
	{
		int min = i;
		for (j = i + 1; j < len; j++)	//遍历未排序的元素
		if (arr[j] < arr[min])			//找到目前最小值
			min = j;					//记录最小值的位置
		swap(&arr[min], &arr[i]);		//做互换(C语言无此函数)
	}
}

冒泡排序程序段(C++)

void bubble_sort(int arr[], int len) 
{
	int i, j, temp;
	for (i = 0; i < len - 1; i++)			//外循环为排序趟数,len个数进行len-1趟
		for (j = 0; j < len - 1 - i; j++) 	//内循环为每趟比较次数,第i趟比较len-i次
			if (arr[j] > arr[j + 1])		//相邻元素比较,若逆序则互换
				swap(&arr[j], &arr[j+1]);
}

插入排序程序段

void insertion_sort(int arr[], int len)
{
	for(int i=1; i<len; i++)			// 从下标为1的元素开始选择合适的位置插入
	{ 
		int key=arr[i]; 				// 记录要插入的数据
		int j=i-1;
		while((j>=0) && (key<arr[j]))	// 从已经排序的序列最右边的开始比较,找到比其小的数
		{ 
			arr[j+1]=arr[j];
			j--;
		}
		arr[j+1]=key; 					//存在比其小的数,插入
	}
}

A - Necklace (UVA 11001)

A - Necklace

中文释义

1
2

• 输入:
输入的每行包含两个数字,Vtotal(0<Vtotal≤60000)和V0(0<V0≤600),含义如上所述。输入以Vtotal = V0=0结束。

• 输出:
输出的每行给出可以制作圆环的数量,使得形成的项链是最长的。如果这一数字不唯一,或者根本无法形成项链,则输出“0”。

解题分析

A-解题分析

本题主要利用题目所给公式D,列出相应的表达式f(n),化简后求导,利用导函数为0时算得表达式的极值点n,利用n与n向下取整的差对n四舍五入即可(注:当差为0.5时说明有两个整数解,应输出0)。

解题代码及注释

#include<stdio.h>
int main()
{
    double vt,v0;				//分别表示Vtootal和V0
    while(scanf("%lf %lf",&vt,&v0)!=EOF && vt+v0) //输入双0则输入结束
    {
        if(vt<=v0)				//当vt <= v0时无法制作圆环,打印0并进入下一次循环
        {
            printf("0\n");	
            continue;
        }else   if(vt<=2*v0)	//根据推导公式,当v0 < vt <= 2*v0时仅能制作一个圆环,直接输出1,提高程序效率。
        {
            printf("1\n");
            continue;
        }
        double n;
        n=vt/(2*v0);
        if(n-(int)n>0.5)			//向上取整
            printf("%d\n",(int)n+1);
        else    if(n-(int)n<0.5)	//向下取整
            printf("%d\n",(int)n);
        else    printf("0\n");		//有两两个整数解
    }
    return 0;
}

B - Bode Plot (POJ 1045, UVA 2284)

Consider the AC circuit below. We will assume that the circuit is in steady-state. Thus, the voltage at nodes 1 and 2 are given by v1 = VS coswt and v2 = VRcos (wt + q ) where VS is the voltage of the source, w is the frequency (in radians per second), and t is time. VR is the magnitude of the voltage drop across the resistor, and q is its phase.
Alt
You are to write a program to determine VR for different values of w. You will need two laws of electricity to solve this problem. The first is Ohm’s Law, which states v2 = iR where i is the current in the circuit, oriented clockwise. The second is i = C d/dt (v1-v2) which relates the current to the voltage on either side of the capacitor. "d/dt"indicates the derivative with respect to t.

Input
The input will consist of one or more lines. The first line contains three real numbers and a non-negative integer. The real numbers are VS, R, and C, in that order. The integer, n, is the number of test cases. The following n lines of the input will have one real number per line. Each of these numbers is the angular frequency, w.
Output
For each angular frequency in the input you are to output its corresponding VR on a single line. Each VR value output should be rounded to three digits after the decimal point.
Sample Input

1.0 1.0 1.0 9
0.01
0.031623
0.1
0.31623
1.0
3.1623
10.0
31.623
100.0

Sample Output

0.010
0.032
0.100
0.302
0.707
0.953
0.995
1.000
1.000

中文释义

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述
本题化简出公式后直接套用即可,主要难点在于数学公式的计算与化简。

解题代码

#include<stdio.h>
#include<math.h>
int main()
{
    double vs,r,c;
    int n,i;
    scanf("%lf %lf %lf %d",&vs,&r,&c,&n);
    for(i=0;i<n;i++)
    {
        double w,vr;
        scanf("%lf",&w);
        vr=r*c*w*vs*sqrt(1/(r*r*c*c*w*w+1));	//根据推导的公式计算Vr
        printf("%.3f\n",vr);
    }
    return 0;
}

C - Symmetric Matrix (UVA 11349)

C

中文释义

在这里插入图片描述

• 输入
输入的第一行给出测试用例数T≤300。接下来的T个测试用例按照 以下方式给出。每个测试用例的第一行给出n,方阵的维数;然后给出n行,每行相应于矩阵的一行,包含n个由空格字符分隔的 元素。第i行的第j个数就是矩阵的元素Mij。
• 输出
对于每个测试用例,输出一行“Test #t: S”,其中t是从1开始的测试用例的编号,如果矩阵是对称的,则S是“Symmetric”;否 则,就是“Non-symmetric”。

解题分析

给出的方阵用二维数组M[100][100]表示。对称矩阵的所有元素都是非负的,并且相对于这个矩阵的中心是对称的,所以,如果有元素是负数,或者存在相对于中心不对称,即M[i][j]!=M[N-1-i][N-1-j],则给出的方阵不是对称的。

解题代码

#include<stdio.h>
long long int m[101][101];		//注意,题目给出的数据范围是64位整型,不能用int!
int main()
{
    int n,T,k;
    scanf("%d",&T);
    for(k=1;k<=T;k++)			//k表示第几组数据
    {
        getchar();				//输入读取多余的字符(回车符),以保证n的正常输入
        scanf("N = %d",&n);
        int i,j,t=1;
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                scanf("%lld",&m[i][j]);	//输入数组
        for(i=0;i<n&&t;i++)				//顺便利用t做标记变量
            for(j=0;j<n&&t;j++)
                if(m[i][j]<0||m[i][j] != m[n-1-i][n-1-j])	//若不满足则将t置为0
                    t=0;
        if(t)							//注意输出格式!一个空格都不能差!保证大小写统一
            printf("Test #%d: Symmetric.\n",k);
        else
            printf("Test #%d: Non-symmetric.\n",k);
    }
    return 0;
}

D - Homogeneous Squares (POJ 2941)

Assume you have a square of size n that is divided into n × n positions just as a checkerboard. Two positions (x1, y1) and (x2, y2), where 1 ≤ x1, y1, x2, y2 ≤ n, are called “independent” if they occupy different rows and different columns, that is, x1 ≠ x2 and y1 ≠ y2. More generally, n positions are called independent if they are pairwise independent. It follows that there are n! different ways to choose n independent positions.

Assume further that a number is written in each position of such an n × n square. This square is called “homogeneous” if the sum of the numbers written in n independent positions is the same, no matter how the positions are chosen. Write a program to determine if a given square is homogeneous!

Input
The input contains several test cases.

The first line of each test case contains an integer n (1 ≤ n ≤ 1000). Each of the next n lines contains n numbers, separated by exactly one space character. Each number is an integer from the interval [−1000000, 1000000].

The last test case is followed by a zero.

Output
For each test case output whether the specified square is homogeneous or not. Adhere to the format shown in the sample output.

Sample Input

2
1 2
3 4
3
1 3 4
8 6 -2
-3 4 0
0

Sample Output

homogeneous
not homogeneous

中文释义

假设您有一个大小为n的正方形,它被划分出n×n个位置,就像一个棋盘。如果存在两个位置(x1, y1)和(x2, y2),其中1≤ x1, y1, x2, y2 ≤n,这两个位置占据不同的行和列,即x1 ≠ x2 并且y1 ≠ y2,则称两个位置是“独立的”。更一般地说,如果n个位置两两间是独立的,则称这n个位置是独立的。因此有n!种不同的选法选择n个独立的位置。

设定在这样一个n×n的正方形的每个位置上都写有一个数。如果不管位置如何选择,写在n个独立位置上的数的和相等,这个正方形称为“homogeneous”。请您编写一个程序来确定一个给出的正方形是否是“homogeneous”的。
• 输入
输入包含若干个测试用例。每个测试用例的第一行给出一个整数n(1≤n≤1000)。接下来的n行每行给出n个数字,数字之间用一个空格字符分隔。每个数字都是在区间[−1000000, 1000000]中的整数。在最后一个测试用例后面跟着一个0。
• 输出
对于每个测试用例,按样例输出中显示的格式,输出给出的正方形是否“homogeneous”。

解题分析

在这里插入图片描述
典型的以点知面类型题目,只需遍历所有2*2方阵即可求得答案。注意示例输出中的输出格式。

解题代码

#include<stdio.h>
int num[1001][1001];
int main()
{
    int n,i,j;
    while(scanf("%d",&n) && n!=0)
    {
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                scanf("%d",&num[i][j]);
        int c=1,sum1,sum2;		//c为矩阵是否符合条件的标志,同时做循环的标志变量。
        for(i=0;i<n-1&&c;i++)	//遍历所有2*2矩阵,计算子矩阵对角线和
            for(j=0;j<n-1&&c;j++)
            {
                sum1=num[i][j]+num[i+1][j+1];
                sum2=num[i][j+1]+num[i+1][j];
                if(sum1!=sum2)	//若此2*2矩阵不符合要求,则将c置为0
                    c=0;
            }
        if(c)					//
            printf("homogeneous\n");
        else    printf("not homogeneous\n");
    }
    return 0;
}

E - Jill Rides Again (UVA 507)

在这里插入图片描述
在这里插入图片描述

中文释义

在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述

解题代码

#include<stdio.h>
int n[20010];			//存储评分
int main()
{
    int tc,r,cot=1;		//tc为路线数目,r为站点数
    scanf("%d",&tc);
    while(tc--)
    {
        scanf("%d",&r);		//输入站点数
        int ans,sum,r1,r2,ans1,ans2;
        ans=sum=r2=ans2=0;
        r1=ans1=1;
        int i,j;
        for(i=1;i<r;i++)	//输入评分
            scanf("%d",&n[i]);
        for(i=1;i<r;i++)	//详细思路见解题分析
        {
            sum+=n[i];		//sum求累加和
            r2=i+1;			//r2为终点坐标
            if(sum<0)		//若sum为负,则清空之前的累加,开始开始新的累加,同时更新起点
            {
                r1=i+1;
                sum=0;
            }else   if(sum>ans || (sum==ans && r2-r1>ans2-ans1))	
            //若这段路评分和最高,则直接记录其累加和与起点终点坐标,若评分和相同则记录路程最长,ans1和ans2为最终答案
            {
                ans=sum;
                ans2=r2;
                ans1=r1;
            }
        }
        //注意输出格式
        if(ans>0)
            printf("The nicest part of route %d is between stops %d and %d\n",cot++,ans1,ans2);
        else    printf("Route %d has no nice parts\n",cot++);
    }
    return 0;
}

F - To the Max (POJ 1050)

Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle.
As an example, the maximal sub-rectangle of the array:

0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
is in the lower left corner:

9 2
-4 1
-1 8
and has a sum of 15.
Input
The input consists of an N * N array of integers. The input begins with a single positive integer N on a line by itself, indicating the size of the square two-dimensional array. This is followed by N2 integers separated by whitespace (spaces and newlines). These are the N2 integers of the array, presented in row-major order. That is, all numbers in the first row, left to right, then all numbers in the second row, left to right, etc. N may be as large as 100. The numbers in the array will be in the range [-127,127].
Output
Output the sum of the maximal sub-rectangle.
Sample Input

4 0 -2 -7 0 9 2 -6 2
-4 1 -4 1 -1

8 0 -2

Sample Output

15

中文释义

给出一个由正整数和负整数组成的二维数组,一个子矩形是指位于整个数组中大小为11或更大的任何连续子数组。矩形的和是该矩形中所有元素的和。在本题中,具有最大和的子矩形被称为最大子矩形。
例如,给出一个二维数组如下:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
最大子矩形是在左下角:
9 2
-4 1
-1 8
矩形的和是15。
• 输入
输入给出一个N
N个整数组成的数组。输入的第一行给出一个正整数N,表示二维正方形数组的大小。后面给出用空白字符(空格和换行符)分隔的N2个整数。这些整数是数组的N2个整数,以行为顺序按行给出。也就是说,首先,第一行从左到右,给出第一行的所有数字;然后,再第二行从左到右,给出第二行的所有数字,以此类推,N的最大值可以是100。数组中的数字的范围是[-127, 127]。
• 输出
输出最大子矩形的和。

解题分析

在这里插入图片描述
在这里插入图片描述

解题代码

#include<stdio.h>
#include<string.h>
#define MAX 102
#define INF 0x3f3f3f3f				//一个非常大的常量,用于下面判断
int d[MAX][MAX],s[MAX];				//d为要输入的原数组,s用于存储合并行后的数组
int MaxArray(int a[],int n);
int main()
{
    int i,j,k,n;
    scanf("%d",&n);
    for(i=0;i<n;i++)				//输入数组
        for(j=0;j<n;j++)
            scanf("%d",&d[i][j]);
    int ans=-INF,temp;
    for(i=0;i<n;i++)
    {
        memset(s,0,sizeof(s));		//清空数组s中的数据
        for(j=i;j<n;j++)
        {
            for(k=0;k<n;k++)		//合并子序列行
                s[k]+=d[j][k];
            temp=MaxArray(s,n);		//求最大子序列和
            if(temp>ans)
                ans=temp;
        }
    }
    printf("%d",ans);
    return 0;
}

int MaxArray(int a[],int n)			//用于返回最大子序列的和
{
    int m=-INF;						//-INF:一个非常小的数字
    int temp=-1,i;
    for(i=0;i<n;i++)				//遍历数组
    {
        if(temp>0)					//类似于上一题,若和为正则继续累加,否则则重新开始
            temp+=a[i];
        else    temp=a[i];
        if(temp>m)					//若连续的子序列和是最大的,则赋值给m
            m=temp;
    }
    return m;
}

G - Who’s in the Middle (POJ 2388)

FJ is surveying his herd to find the most average cow. He wants to know how much milk this ‘median’ cow gives: half of the cows give as much or more than the median; half give as much or less.

Given an odd number of cows N (1 <= N < 10,000) and their milk output (1…1,000,000), find the median amount of milk given such that at least half the cows give the same amount of milk or more and at least half give the same or less.
Input
Line 1: A single integer N

Lines 2…N+1: Each line contains a single integer that is the milk output of one cow.
Output
Line 1: A single integer that is the median milk output.
Sample Input

5
2
4
1
3
5

Sample Output

3

Hint
INPUT DETAILS:
Five cows with milk outputs of 1…5
OUTPUT DETAILS:
1 and 2 are below 3; 4 and 5 are above 3.

中文释义

FJ调查他的奶牛群,他要找到最一般的奶牛,看最一般的奶牛产多少牛奶:一半的奶牛产奶量大于或等于这头奶牛,另一半的奶牛产奶量小于或等于这头奶牛。

给出奶牛的数量:奇数N (1≤N<10,000)及其产奶量(1…1,000,000),找出位于产奶量中点的奶牛,要求一半的奶牛产奶量大于或等于这头奶牛,另一半的奶牛产奶量小于或等于这头奶牛。
输入
第1行: 整数N
第2行到第N+1行:每行给出一个整数,表示一头奶牛的产奶量。
输出
一个整数,位于中点的产奶量。
提示
对于样例输入,5头奶牛,产奶量为1…5;因为1和2低于3,4和5在3之上,所以输出3。

解题分析

递增(或递减)排序N头奶牛的产奶量,排序后的中间元素即为位于中点的产奶量。
参照选择排序、冒泡排序、插入排序C语言程序段,对输入的产奶量序列进行排序,然后输出中点的产奶量。
本题是本次试题中最简单的一道题,运用冒泡排序后输出中间的数值即可。

解题代码

#include<stdio.h>
int main()
{
    int n,i,j,num[10001],t;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&num[i]);
    for(i=1;i<n;i++)				//运用冒泡排序将数据排序
        for(j=0;j<n-i;j++)
            if(num[j]<num[j+1])		
            {
                t=num[j];
                num[j]=num[j+1];
                num[j+1]=t;
            }
    printf("%d",num[n/2]);			//输出排序后位于中间的数据即可
    return 0;
}

H - Train Swapping (UVA 299)

在这里插入图片描述

中文释义

在老旧的火车站,您可能还会遇到“列车交换员”。列车交换员是铁路的一个工种,其工作是对列车车厢重新进行安排。

一旦车厢要以最佳的序列被安排,列车司机要将车厢一节接一节地将它们在要卸货的车站留下。
“列车交换员” 是一个在靠近铁路桥的车站执行这一任务的人,不是将桥垂直吊起,而是将桥围绕着河中心的桥墩进行旋转。将桥旋转了90度后,船可以从桥墩的左边或者右边通过。

一个列车交换员在桥上有两节车厢的时候也可以旋转。将桥旋转180度,车厢可以转换位置,使得他可以对车厢进行重新排列。(车厢也将掉转方向,但车厢两个方向都可以移动,所以这一情况不用考虑)。

现在几乎所有的列车交换员已经故去,铁路公司要将他们的操作自动化。要开发部分的程序的功能是对一列给定的列车按给定次序排列,要确定两个相邻车厢的最少的交换次数,请您编写程序。

• 输入
输入的第一行给出测试用例的数目(N)。每个测试用例有两行,第一行给出整数L,表示列车车厢的数量(0≤L≤50),第二行给出一个从1到L的排列,给出车厢的当前排列次序。要按数字的升序重新排列这些车厢:先是1,再是2,等等,最后是L。
• 输出
对每个测试用例输出一句句子:‘Optimal train swapping takes S swaps.’,其中S是一个整数。

解题分析

输入列车的排列次序a[1]‥a[m]后,对a[ ]进行递增排序,在排序过程中数据互换的次数即为问题解。由于m的上限仅为50,因此使用冒泡排序亦满足时效要求。

解题代码

#include<stdio.h>
void swap(int *x,int *y);
int main()
{
    int N;
    scanf("%d",&N);
    while(N--)
    {
        int L,num[51],i,j,count=0;
        scanf("%d",&L);
        for(i=0;i<L;i++)
            scanf("%d",&num[i]);
        for(i=1;i<L;i++)				//冒泡法排序
            for(j=0;j<L-i;j++)
                if(num[j]>num[j+1])
                {
                    swap(num+j,num+j+1);
                    count++;			//计数
                }
        printf("Optimal train swapping takes %d swaps.\n",count);
    }
    return 0;
}

void swap(int *x,int *y)				//交换变量
{
    int temp;
    temp=*x;
    *x=*y;
    *y=temp;
}

I - DNA Sorting (POJ 1007)

One measure of “unsortedness” in a sequence is the number of pairs of entries that are out of order with respect to each other. For instance, in the letter sequence “DAABEC”, this measure is 5, since D is greater than four letters to its right and E is greater than one letter to its right. This measure is called the number of inversions in the sequence. The sequence “AACEDGG” has only one inversion (E and D)—it is nearly sorted—while the sequence “ZWQM” has 6 inversions (it is as unsorted as can be—exactly the reverse of sorted).

You are responsible for cataloguing a sequence of DNA strings (sequences containing only the four letters A, C, G, and T). However, you want to catalog them, not in alphabetical order, but rather in order of “sortedness”, from “most sorted” to “least sorted”. All the strings are of the same length.
Input
The first line contains two integers: a positive integer n (0 < n <= 50) giving the length of the strings; and a positive integer m (0 < m <= 100) giving the number of strings. These are followed by m lines, each containing a string of length n.
Output
Output the list of input strings, arranged from “most sorted” to “least sorted”. Since two strings can be equally sorted, then output them according to the orginal order.
Sample Input

10 6
AACATGAAGG
TTTTGGCCAA
TTTGGCCAAA
GATCAGATTT
CCCGGGGGGA
ATCGATGCAT

Sample Output

CCCGGGGGGA
AACATGAAGG
GATCAGATTT
ATCGATGCAT
TTTTGGCCAA
TTTGGCCAAA

中文释义

在一个字符串中逆序数是在该串中与次序相反的字符对的数目。例如,字母序列“DAABEC”的逆序数是5,因为D比它右边的4个字母大,而E比它右边的1个字母大。序列“AACEDGG”的逆序数是1(E和D),几乎已经排好序了。而序列“ZWQM”的逆序数是6,完全没有排好序。

您要对DNA字符串序列进行分类(序列仅包含4个字母A,C,G和T)。然而,分类不是按字母顺序,而是按“排序”的次序,从“最多已排序”到“最少已排序”进行排列。所有的字符串长度相同。

• 输入
第一行是两个正整数:n(0<n≤50)给出字符串的长度,m(0<m≤100)给出字符串的数目。后面是m行,每行为长度为n的字符串。

• 输出
对输入字符串按从“最多已排序”到“最少已排序”输出一个列表。两个字符串排序情况相同,则按原来的次序输出。

解题分析

“最多已排序”的串指的是串中逆序对数最少的串;而串中逆序对数最多的串就是所谓的“最少已排序”的串。所以设DNA序列为字符串数组s,其中第i个DNA串为s[i];逆序对数为f[i],1≤i≤m。
首先,使用冒泡排序,统计每个DNA串的逆序对数f[i];然后,使用插入排序,按逆序对数递增排序s;最后,输出s[1]‥s[m]。

解题代码

#include<stdio.h>
#include<string.h>
int main()
{
    int n,m,i,j,k,temp,f[120];	//temp为插入排序时待插入的整型数据
    char s[120][51],temps[51];	//temps为插入排序时待插入的字符串数据
    scanf("%d %d%*c",&n,&m);
    for(i=0;i<m;i++)			//输入并利用冒泡排序统计逆序字母数
    {
        gets(s[i]);	
        f[i]=0;
        for(j=0;j<n-1;j++)		//冒泡排序,统计数量
            for(k=j+1;k<n;k++)
                if(s[i][k]<s[i][j])
                    f[i]++;
    }
    for(i=1;i<m;i++)			//插入排序,较为重要,详细说明
    {
        if(f[i]>=f[i-1])		//若逆序数大于等于前一个,则跳过该数,判断下一个
            continue;
        j=i;					//记录待插入数据的下标
        temp=f[i];				//记录待插入的整型数据
        strcpy(temps,s[i]);		//记录待插入的字符串数据
        while(temp<f[j-1]&&j>=1)//判断上一个数字是否大于该数字,将符合条件的所有数据向后平移,以空出可插入的空间
        {
            f[j]=f[j-1];
            strcpy(s[j],s[j-1]);
            j--;
        }
        f[j]=temp;				//将待插入的数据放入刚平移后空出的空间
        strcpy(s[j],temps);		//同理,放入字符串数据
    }							//输出排序后的数据
    for(i=0;i<m;i++)
        puts(s[i]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值