HIST ICPC校内选拔训练赛(纯思维题)
A.Tit for Tat
思路:首先不知道字典序的可以先baidu一下字典序
对于数字1、2、3…n的排列,不同排列的先后关系是从左到右逐个比较对应的数字的先后来决定的。例如对于5个数字的排列 12354和12345,排列12345在前,排列12354在后。按照这样的规定,5个数字的所有的排列中最前面的是12345,最后面的是 54321。
我们让最高位(也就是前面的元素)尽量小,但不能小于0(一旦小于0就跳出循环),同时还要使a[i]+1
,a[i]
越靠后,影响就越小。
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-->0) {
int n = sc.nextInt();
int k = sc.nextInt();
int a[] = new int[n];
for(int i=0;i<n;i++) {
a[i] = sc.nextInt();
}
boolean flag = false;
int l = 0;int r = n-1;
while(l<r&&k>0) {
for(int i=0;i<n;i+=0) {
if(a[i]!=0&&k>0) {
a[i]--;
k--;
a[n-1]++;
}else {
l++;
i++;
continue;
}
}
}
System.out.print(a[0]);
for(int j=1;j<n;j++) {
System.out.print(" "+a[j]);
}System.out.println();
}
}
}
BA.rray and Peaks
思路:这道题的大致就是将一条y=x的曲线经过有限次操作,得到一条有k个极大值点的曲线。下面举一个例子
1 2 3 4 5 → 1 2 3 5 4 → 1 3 2 5 4
通过这条直线的性质可以知道,从后往前与相邻的元素进行交换会得到一个极大值点,从前往后与相邻元素进行交换,会得到一个极小值点。注意,一个元素只能交换一次。
这里边有一种特殊情况,那就是n<k*2+1
这种,这种是不满足条件的。
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-->0) {
int n = sc.nextInt();
int k = sc.nextInt();
int a[] = new int[n+1];
for(int i=1;i<=n;i++) {
a[i] = i;
}
if(n<k*2+1&&k!=0) {
System.out.println(-1);
}else {
for(int i=1;i<=2*k;i+=2) {
int x = a[n+1-i];
a[n+1-i]=a[n-i];
a[n-i]=x;
}
System.out.print(a[1]);
for(int i=2;i<=n;i++) {
System.out.print(" "+a[i]);
}System.out.println();
}
}
}
}
C.Candies and Two Sisters
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-->0) {
int n = sc.nextInt();
if(n%2!=0) {
System.out.println(n/2);
}else {
System.out.println(n/2-1);
}
}
}
}
D.Sum of Medians
思路:分情况讨论
1.n=1
时,将所有项加到一起就是答案
2.n!=1
时,因为他是取的每一组中第n/2个数,所以对于每一小组来说如果n是奇数,可以忽略前n/2项,如果n是偶数,可以忽略前n/2-1项。然后对于原数列,就是相当于忽略了前k*y=n%2==0?n/2-1:n/2
项,然后剩余项,每隔n/2
项取一次即可
import java.util.Arrays;
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-->0) {
int n = sc.nextInt();
int k = sc.nextInt();
long a[] = new long[n*k];
for(int i=0;i<n*k;i++) {
a[i] = sc.nextLong();
}
Arrays.sort(a);
int x = n/2;
long s = 0;
if(n==1) {
for(int i=0;i<n*k;i++) {
s+=a[i];
}
}
else if(n==2) {
for(int i=0;i<n*k;i+=2) {
s+=a[i];
}
}else {
x++;
int y = n%2==0?n/2-1:n/2;
for(int i=k*y;i<n*k;i+=x) {
s+=a[i];
}
}
System.out.println(s);
}
}
}
E.Rock and Lever
思路:这道题主要是利用了位运算的性质以及排列组合的知识。
位运算性质:对于本题来说,用到了&
运算以及^
运算。&
就是同时为1时才为1,其余为0.^
是01
或者10
是得到1,其余为0.
而对于本题来说只需要关注最高位即可,如果两个数的二进制的位数相同,那么最高位都是1,^
之后就为0了,而&
之后还是1,所以二进制位数相同的两个数a&b>=a^b
。如果位数不同,那么其中必定有一个数是位数不够用0补的,也就是会出现一个0,一个1的情况,这种情况下a&b<a^b
。
接下来就是排列组合的知识了,两两结对,会发现所有情况就是前n-1项和(n为二进制中相同位数的个数)
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-->0) {
long s[] = new long[33];
int n = sc.nextInt();
int a[] = new int[n];
for(int i=0;i<n;i++) {
a[i] = sc.nextInt();
String str = Integer.toBinaryString(a[i]);
s[str.length()]++;
}
long ans = 0;
for(int i=1;i<=32;i++) {
if(s[i]>1)ans+=((s[i]-1)*(s[i]-1)+s[i]-1)/2;
}
System.out.println(ans);
}
}
}
F.Berland Poker
思路:第一个人要给他尽量多的王,其余人平均分王,如果还有剩余的(也就是
剩余的王%人数!=0
),就再给一部分人平均分一下。
import java.util.Arrays;
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-->0) {
int n = sc.nextInt();
int m = sc.nextInt();
int k = sc.nextInt();
int a[] = new int[k];
if(m>n/k) {
a[0]=n/k;
m-=n/k;
}else {
System.out.println(m);
continue;
}
while(m>0) {
for(int i=1;i<k&&m>0;i++) {
a[i]++;
m--;
}
}
Arrays.sort(a);
System.out.println(a[k-1]-a[k-2]);
}
}
}
G.Buying Shovels
思路:这道题的本质就是求n在1-k范围内的最大因子。
由于数值比较大,所以用遍历到 n \sqrt{n} n,如果n%i==0
,那么将n/i
以及i
都存在ArrayList里,接下来遍历ArrayList找出小于k的最大的元素即可。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
//求小于k的最大约数
public class G_BuyingShovels {
public static int n;
public static int k;
public static ArrayList<Integer>al = new ArrayList<>();
public static void check(int x) {
al.clear();//得先清一下
int max = 1;
for(int i=1;i*i<=x;i++) {
if(x%i==0) {
al.add(i);
al.add(x/i);
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-->0) {
n = sc.nextInt();
k = sc.nextInt();
int max = 1;
if(k>=n) {
System.out.println(1);
}else {
check(n);
Collections.sort(al);
for(int i=0;i<al.size();i++) {
if(al.get(i)<=k) {
max = Math.max(max,al.get(i));
}
}
System.out.println(n/max);
}
}
}
}
I.Alice, Bob and Candies
这道题我感觉洛谷的题目翻译不是很清晰,可以自己百度翻译,看懂题意之后模拟即可。
import java.util.Arrays;
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-->0) {
int n = sc.nextInt();
int s[] = new int[n];
for(int i=0;i<n;i++) {
s[i] = sc.nextInt();
}
int a = 0;//A总得分
int b = 0;//B总得分
int index = 0;//记录回合数
int nowa = 0;//记录当前a的增量
int nowb = 0;//记录当前b的增量
int l = 0;
int r = n-1;
while(l<=r) {
while(l<=r&&nowa<=nowb) {
a+=s[l];
nowa+=s[l];
l++;
}
if(l<=r)index++;
nowb=0;
while(l<=r&&nowb<=nowa) {
b+=s[r];
nowb+=s[r];
r--;
}
if(l<=r)index++;
nowa=0;
}
System.out.println(index+1+" "+a+" "+b);
}
}
}
J.Anti-Sudoku
这道题是一个思维题,对于一个数独来说,对于一行一列来说,如果想使这一行一列刚好有两个相同的元素,那么只需要将这一行一列交点处的值换成与当前值不同的即可(1到9)。并且还要考虑每一个小的九宫格,所以就要求每一个小的九宫格里有一个要被替换,而且被替换的元素必须是不同行不同列的。
扩展到九行九列,就是下图。
替换黑色的部分即可。(答案不唯一)
import java.util.Scanner;
//要用StringBuilder
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
sc.nextLine();
while(t-->0) {
char a[][] = new char[9][9];
for(int i=0;i<9;i++) {
String str = sc.nextLine();
a[i] = str.toCharArray();
}
a[0][0]=a[0][0]=='1'?'2':'1';
a[1][3]=a[1][3]=='1'?'2':'1';
a[2][6]=a[2][6]=='1'?'2':'1';
a[3][1]=a[3][1]=='1'?'2':'1';
a[4][4]=a[4][4]=='1'?'2':'1';
a[5][7]=a[5][7]=='1'?'2':'1';
a[6][2]=a[6][2]=='1'?'2':'1';
a[7][5]=a[7][5]=='1'?'2':'1';
a[8][8]=a[8][8]=='1'?'2':'1';
StringBuilder sb = new StringBuilder();
for(int i=0;i<9;i++) {
for(int j=0;j<9;j++) {
sb.append(a[i][j]);
}
if(i!=8)sb.append("\n");
}
System.out.println(sb);
}
}
}