写在开头…由于测试点问题,在本地eclipse上运行无错。但可能由于个人粗心,导致出现边界错误,或者思路错误。但是在官网和学校的训练网站上并没有搭建17、18年题目的环境,所以我也无法保证我写的完全是正确的,个人保证最大程度的正确。如果出现错误,欢迎指教,谢谢~
一、标题:日期问题
小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在1960年1月1日至2059年12月31日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。
比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。
给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?
输入
一个日期,格式是"AA/BB/CC"。 (0 <= A, B, C <= 9)
输入
输出若干个不相同的日期,每个日期一行,格式是"yyyy-MM-dd"。多个日期按从早到晚排列。
样例输入
02/03/04
样例输出
2002-03-04
2004-02-03
2004-03-02
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
思路:全排列+合理的时间
package lijie_17;
import java.util.Scanner;
public class s_7 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String s=sc.next();
String []ss=s.split("/");
// for(int i=0;i<=2;i++){
// System.out.print(ss[i]+" ");
// }
// System.out.println();
// String []num={"98","02","08"};
demo(ss, 0, 2);
}
public static void format(String[] num){
int []nums=new int [3];
nums[0]=Integer.valueOf(num[0]);
nums[1]=Integer.valueOf(num[1]);
nums[2]=Integer.valueOf(num[2]);
// for(int i=0;i<=2;i++){
// System.out.print(nums[i]+" ");
// }
// System.out.println();
if(nums[1]==1||nums[1]==3||nums[1]==5||nums[1]==7||nums[1]==8||nums[1]==10||nums[1]==11){
if(nums[2]<=31){
if(nums[0]>=60){
nums[0]+=1900;
}else{
nums[0]+=2000;
}
String m="";
if(nums[1]>=10){
m=String.valueOf(nums[1]);
}else{
m="0"+nums[1];
}
String d="";
if(nums[2]>=10){
d=String.valueOf(nums[2]);
}else{
d="0"+nums[2];
}
String s=String.valueOf(nums[0])+"-"+m+"-"+d;
System.out.println(s);
}
}else{
if(nums[2]<=30){
if(nums[0]>=60){
nums[0]+=1900;
}else{
nums[0]+=2000;
}
String m="";
if(nums[1]>=10){
m=String.valueOf(nums[1]);
}else{
m="0"+nums[1];
}
String d="";
if(nums[2]>=10){
d=String.valueOf(nums[2]);
}else{
d="0"+nums[2];
}
String s=String.valueOf(nums[0])+"-"+m+"-"+d;
System.out.println(s);
}
}
}
public static void demo(String []num,int start,int end){
if(start==end){
// for(int i=0;i<=end;i++){
// System.out.print(num[i]+" ");
// }
// System.out.println();
if(Integer.parseInt(num[1])>0&&Integer.parseInt(num[1])<13&&Integer.parseInt(num[2])>0&&Integer.parseInt(num[2])<32){
//闰年
if(Integer.parseInt(num[0])%400==0||(Integer.parseInt(num[0])%4==0&&Integer.parseInt(num[0])%100!=0)){
if(Integer.parseInt(num[1])==2&&Integer.parseInt(num[2])<=29){
format(num);
}else if(Integer.parseInt(num[1])!=2){
format(num);
}
}else{
if(Integer.parseInt(num[1])==2&&Integer.parseInt(num[2])<=28){
format(num);
}else if(Integer.parseInt(num[1])!=2){
format(num);
}
}
}
}
for(int i=start;i<=end;i++){
swap(num, i, start);
demo(num, start+1, end);
swap(num, i, start);
}
}
public static void swap(String []num,int i,int j){
String t=num[i];
num[i]=num[j];
num[j]=t;
}
}
二、标题:包子凑数
小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
输入
第一行包含一个整数N。(1 <= N <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)
输出
一个整数代表答案。如果凑不出的数目有无限多个,输出INF。
例如,
输入:
2
4
5
程序应该输出:
6
再例如,
输入:
2
4
6
程序应该输出:
INF
样例解释:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
对于样例2,所有奇数都凑不出来,所以有无限多个。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
思路: 感觉是历届 题目 买不到的数目的升级版。多了一个INF,如果数的最大公约数不是1,那就是INF,因为数之间存在间隔了
package lijie_17;
import java.util.Arrays;
import java.util.Scanner;
public class s_8 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int []num=new int[n];
for(int i=0;i<n;i++){
num[i]=sc.nextInt();
}
if(zdgy(num)){
demo(num);
}else{
System.out.println("INF");
}
}
/**
* 判断最大公约数是不是1
* */
public static boolean zdgy(int []num){
Arrays.sort(num);
int a=num[0];
for(int i=2;i<=a;i++){
int flag=0;
for(int j=0;j<num.length;j++){
if(num[j]%i==0){
flag++;
}
}
if(flag==num.length){
return false;
}
}
return true;
}
public static void demo(int []num){
Arrays.sort(num);
int []a=new int[100001];
for(int i=0;i<num.length;i++){
a[num[i]]=1;
}
int res=0;
for(int i=1;i<100001;i++){
int flag=0;
for(int j=0;j<num.length;j++){
if(a[i]!=1){
if(i-num[j]>0&&a[i-num[j]]==1){
a[i]=1;
flag=1;
}
}else{
flag=1;
}
}
if(flag==0){
res++;
// System.out.println(i);
}
}
System.out.println(res);
}
}
三、标题: 分巧克力
儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:
- 形状是正方形,边长是整数
- 大小相同
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。
输出
输出切出的正方形巧克力最大可能的边长。
样例输入:
2 10
6 5
5 6
样例输出:
2
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
思路:模拟切的过程,j从1开始,到最短的边长结束。如果宽大于j,那就j行j行的切,如果小于j,那就结束。
import java.util.Scanner;
public class s_9 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
int [][]num=new int[n][2];
int min=100002;
for(int i=0;i<n;i++){
int a=sc.nextInt();
int b=sc.nextInt();
if(a<=b){
num[i][0]=a;
num[i][1]=b;
min=Math.min(a, min);
}else{
num[i][0]=b;
num[i][1]=a;
min=Math.min(b, min);
}
}
int res=-1;
for(int i=1;i<min;i++){
int kuai=0;
for(int j=0;j<n;j++){
int t=num[j][0];
int tt=num[j][1];
while(t>=i){
kuai+=tt/i;
t-=i;
}
}
if(kuai>=m){
res=Math.max(i, res);
// System.out.println(i+" "+kuai);
}
}
System.out.println(res);
}
}
四、标题:复数幂
设i为虚数单位。对于任意正整数n,(2+3i)^n 的实部和虚部都是整数。
求 (2+3i)^123456 等于多少? 即(2+3i)的123456次幂,这个数字很大,要求精确表示。
答案写成 “实部±虚部i” 的形式,实部和虚部都是整数(不能用科学计数法表示),中间任何地方都不加空格,实部为正时前面不加正号。(2+3i)^2 写成: -5+12i,
(2+3i)^5 的写成: 122-597i
思路:(2+3i)的n次方都可以记作(a+bi)(2+3i),不同的只是a+bi中的a和b。从2次方开始模拟,就可以得到最终答案13483137+1100011648i
我当时担心的问题还有 是否存在4个一循环,毕竟i的4次方才为1。分别拿(2+3i)的2次方3次方进行计算,发现不存在这个问题,是符合(a+bi)(2+3i)的。
我更新~在我哥的指点下,发现用long会变化,那就证明int是不可以的…思路大概这样…但是要用BigInteger
package lijie_18;
public class s_3 {
public static void main(String[] args) {
int a=2;
int b=3;
int n=123456;
int ta,tb;
for(int i=2;i<=n;i++){
ta=2*a-3*b;
tb=3*a+2*b;
a=ta;
b=tb;
}
if(b>=0){
System.out.println(a+"+"+b+"i");
}else{
System.out.println(a+""+b+"i");
}
}
}
五、标题:快速排序
以下代码可以从数组a[]中找出第k小的元素。
它使用了类似快速排序中的分治算法,期望时间复杂度是O(N)的。
请仔细阅读分析源码,填写划线部分缺失的内容。
import java.util.Random;
public class Main{
public static int quickSelect(int a[], int l, int r, int k) {
Random rand = new Random();
int p = rand.nextInt(r - l + 1) + l;
int x = a[p];
int tmp = a[p]; a[p] = a[r]; a[r] = tmp;
int i = l, j = r;
while(i < j) {
while(i < j && a[i] < x) i++;
if(i < j) {
a[j] = a[i];
j--;
}
while(i < j && a[j] > x) j--;
if(i < j) {
a[i] = a[j];
i++;
}
}
a[i] = x;
p = i;
if(i - l + 1 == k) return a[i];
if(i - l + 1 < k) return quickSelect( _________________________________ ); //填空
else return quickSelect(a, l, i - 1, k);
}
public static void main(String args[]) {
int [] a = {1, 4, 2, 8, 5, 7};
System.out.println(quickSelect(a, 0, 5, 4));
}
}
答案:return quickSelect(a,i+1,r,k-i+l-1);
思路:能看出来是随机选择一个数,将比这个数小的放在左边,比这个数大的放在右边。如果这个数在第k位,那就是我们要求的数。如果比k大,就在k的右边找。问题是,在比k小,在左边找的时候的循环函数。四个空,可以确定两个,a和r。剩下的两个是左边界和第几小。挨个试吧…
六、标题:递增三元组
给定三个整数数组
A = [A1, A2, … AN],
B = [B1, B2, … BN],
C = [C1, C2, … CN],
请你统计有多少个三元组(i, j, k) 满足:
- 1 <= i, j, k <= N
- Ai < Bj < Ck
【输入格式】
第一行包含一个整数N。
第二行包含N个整数A1, A2, … AN。
第三行包含N个整数B1, B2, … BN。
第四行包含N个整数C1, C2, … CN。
对于30%的数据,1 <= N <= 100
对于60%的数据,1 <= N <= 1000
对于100%的数据,1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000
【输出格式】
一个整数表示答案
【输入样例】
3
1 1 1
2 2 2
3 3 3
【输出样例】
27
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
思路: a[i]代表a中i出现的次数,b[i]代表b中i出现次数。c同理。ai<bj<ck
那就统计a[]中比ai小的有多少个。举例吧…
1 1 1 —a[]
2 2 2 —b[]
3 3 3—c[]
初始时,a[1]=3,b[2]=3,c[3]=3。对于b来说,比2小的是1,那就对应a[1]=3,所以b[2]*=a[1],b[2]=9,代表以b的2结尾有9种可能性(题目隐含可以包含重复的)。同理,c[3]*=b[2],即27
但是范围扩大,如果是
1 2 3
2 5 6
5 7 8
这种,那么a[1]=1,a[2]=1,a[3]=1.b[2]=1,b[5]=1,b[6]=1,c[5]=1,c[7]=1,c[8]=1
如果选择以b中6结尾,那么a中的三个都可以选择,可以表示成a[2]=1,a[3]=2,a[5]=3;代表着着在选择a[i]的时候可以有多少种可能性。将b[]也进行上述变化。可得代码
package lijie_18;
import java.util.Scanner;
public class s_6 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int []a=new int[100001];
for(int i=0;i<n;i++){
a[sc.nextInt()]++;
}
int []b=new int[100001];
for(int i=0;i<n;i++){
b[sc.nextInt()]++;
}
int []c=new int[100001];
for(int i=0;i<n;i++){
c[sc.nextInt()]++;
}
for(int i=1;i<100001;i++){
a[i]+=a[i-1];
// System.out.print(a[i]+"!!!");
}
for(int i=1;i<100001;i++){
b[i]*=a[i-1];
b[i]+=b[i-1];
// System.out.print(b[i]+"*");
}
// System.out.println();
int res=0;
for(int i=1;i<100001;i++){
c[i]*=b[i-1];
res+=c[i];
// System.out.print(c[i]+"@");
}
// System.out.println();
System.out.println(res);
}
}
七、标题:螺旋折线
如图p1.pgn所示的螺旋折线经过平面上所有整点恰好一次。
对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。
例如dis(0, 1)=3, dis(-2, -1)=9
给出整点坐标(X, Y),你能计算出dis(X, Y)吗?
【输入格式】
X和Y
对于40%的数据,-1000 <= X, Y <= 1000
对于70%的数据,-100000 <= X, Y <= 100000
对于100%的数据, -1000000000 <= X, Y <= 1000000000
【输出格式】
输出dis(X, Y)
【输入样例】
0 1
【输出样例】
3
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
思路:找到每半圈cir的长度相等,和下半圈x,y方向相反,模仿螺旋线的轨迹,运用while循环
package lijie_18;
public class s_7 {
public static void main(String[] args) {
int x=0,y=0;//初始点
int resx=2,resy=0;//目标点
int dirx=-1,diry=1;//方向
int res=0;//步数
int cir=1;//每一圈步数
while(true){
int tcir=cir;
if(x==resx&&y==resy){
System.out.println(res);
break;
}
int flag=0;
while(cir-->0){
x+=dirx;
res++;
if(x==resx&&y==resy){
System.out.println(res);
flag=1;
break;
}
}
if(flag==1){
break;
}
cir=tcir;
while(cir-->0){
y+=diry;
res++;
if(x==resx&&y==resy){
System.out.println(res);
flag=1;
break;
}
}
if(flag==1){
break;
}
cir=tcir;
cir++;
dirx=-dirx;
diry=-diry;
}
}
}
八、标题:日志统计
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。其中每一行的格式是:
ts id
表示在ts时刻编号id的帖子收到一个"赞"。
现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。
具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。
【输入格式】
第一行包含三个整数N、D和K。
以下N行每行一条日志,包含两个整数ts和id。
对于50%的数据,1 <= K <= N <= 1000
对于100%的数据,1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000
【输出格式】
按从小到大的顺序输出热帖id。每个id一行。
【输入样例】
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
【输出样例】
1
3
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
思路:单对于时间那个,我想到的是time【i】代表i时刻的点赞次数。但是有未知个id,而且在输出id的时候要求从小到大,我就感觉TreeMap是个好东西,再把id和它相对应的一长串time[]构造类。在下面代码中,一定要注意,不要把时间相减从小到大开始。
这个和上面那个超不超时…我已经很尽力了…没有测试平台,换句话,如果有测试平台,对于后天就要比赛的我来说,…也改不了了…希望读者加油~
package lijie_18;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeMap;
public class s_8 {
static TreeMap<Integer, point> tm;
public static void main(String[] args) {
int n,d,k;
tm=new TreeMap<Integer, point>();
Scanner sc=new Scanner(System.in);
n=sc.nextInt();//n个帖子
d=sc.nextInt();//时间段
k=sc.nextInt();//点赞数
for(int i=0;i<n;i++){
int time=sc.nextInt();
int id=sc.nextInt();
if(tm.containsKey(id)){
tm.get(id).setTime(time);
}else{
point p=new point(id);
p.setTime(time);
tm.put(id,p);
}
}
Set st=tm.keySet();
Iterator it=st.iterator();
while(it.hasNext()){
point p=tm.get(it.next());
for(int i=1;i<p.time.length;i++){
p.time[i]+=p.time[i-1];
}
for(int i=p.time.length-1;i>=d-2;i--){
p.time[i]-=p.time[i-d+2];
// if(p.id==10){
System.out.println(p.time[i]+" "+i+" "+k);
for(int ii=0;ii<20;ii++){
System.out.print(p.time[ii]+" ");
}
System.out.println();
// }
if(p.time[i]>=k){
System.out.println(p.id);
break;
}
}
}
}
}
class point{
int id;
int[]time;
public point(int id){
this.id=id;
time=new int[100001];
}
public void setTime(int t){
this.time[t]++;
}
}