(1)第一题:洗牌
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Problem Description:
一、题目:
洗牌在生活中十分常见,现在需要写一个程序模拟洗牌的过程。
现在需要洗2n张牌,从上到下依次是第1张,第2张,第3张一直到第2n张。首先,我们把这2n张牌分成两堆,左手拿着第1张到第n张(上半堆),右手拿着第n+1张到第2n张(下半堆)。接着就开始洗牌的过程,先放下右手的最后一张牌,再放下左手的最后一张牌,接着放下右手的倒数第二张牌,再放下左手的倒数第二张牌,直到最后放下左手的第一张牌。接着把牌合并起来就可以了。
例如有6张牌,最开始牌的序列是1,2,3,4,5,6。首先分成两组,左手拿着1,2,3;右手拿着4,5,6。在洗牌过程中按顺序放下了6,3,5,2,4,1。把这六张牌再次合成一组牌之后,我们按照从上往下的顺序看这组牌,就变成了序列1,4,2,5,3,6。
现在给出一个原始牌组,请输出这副牌洗牌k次之后从上往下的序列。
输入
第一行一个数T(T<=100),表示数据组数。对于每组数据,第一行两个数n,k(1<=n,k<=100),接下来一行有2n个数a1,a2,...,a2n(1<=ai<=1000000000)。表示原始牌组从上到下的序列。
输出
对于每组数据,输出一行,最终的序列。数字之间用空格隔开,不要在行末输出多余的空格。
样例输入
3
3 1
1 2 3 4 5 6
3 2
1 2 3 4 5 6
2 2
1 1 1 1
样例输出
1 4 2 5 3 6
1 5 4 3 2 6
1 1 1 1
二、代码(两种方法):方法一思路:
1)洗牌的头和尾的牌的位置是不变的,一次的排序中,头和尾的数字不需要变化,从head=1,tail=length-2开始,中间的为mid;每次的循环后,head++;tail--; 当tail-head<0时,一次排序结束;在每次的循环中,tail的数据与mid 的数据交换位置,交换后,tail--,mid--;一直到mid<head的时候停止交换数据;
2)k指的是洗几次牌,也就是重复几次的1)操作
这个算法是以时间复杂度换取空间复杂度
import java.util.*; public class Main { public static void sortStr(int[] a,int k) { while(k>0) { int tail=a.length-2; int head=1; int mid=a.length/2-1; while(tail-head>=0) { stangeStr(a,head,tail, mid); head++; tail--; } k--; } for(int i=0;i<a.length;i++) { System.out.print(a[i]); } } public static void stangeStr(int a[],int head,int tail,int mid) { int temp; while (mid>=head) { temp = a[tail]; a[tail] = a[mid]; a[mid] = temp; mid--; tail--; } } public static void main(String args[]) { Scanner cin = new Scanner(System.in); int n, k; int c=cin.nextInt(); while(c>0) { n = cin.nextInt(); k = cin.nextInt(); int n2=2*n; int[] a=new int[n2]; for(int i=0;i<n2;i++) a[i]= cin.nextInt(); sortStr(a,k); c--; } } }方法二思路:
将输入的数组a的前一半存储在arry1中,后面的一半数据存储在arry2中,然后一次将arry1和arry2中的数据轮流插入a 中,是从末位的数据开始插入a中的尾部;例如,第一次先把arry2中的最后一个数据插入a中的最后一个位置,下一次将arry1中的最后一个数据插入a中倒数第二个位置;接着将arry2中的倒出第二个数插入a中的倒数第三个位置,arry1中的倒数第二个数插入a中的倒数第四个位置;一次循环一直到arry1和arry2中的数据全部插入a中,这时a中的数据就是一次洗牌后的数据次序。
该方法是以空间换取时间
代码如下:
import java.util.*; public class Main { public static void sortStr(int[] a,int k) { int len=a.length; int len1=len/2; while(k>0) { int[] arry1 = new int[len1]; int[] arry2 = new int[len1]; int i = 0; for (; i < len1; i++) arry1[i] = a[i]; for (int j=0; i < len; j++,i++) arry2[j] = a[i]; int tail1 = len1 - 1; int tail2 = len1 - 1; for (int j = len - 1; j >= 0; j--) { if (j % 2 == 0) a[j] = arry1[tail1--]; else a[j] = arry2[tail2--]; } k--; } for(int j=0;j<len;j++) System.out.print(a[j]); } public static void main(String args[]) { Scanner cin = new Scanner(System.in); int n, k; int c=cin.nextInt(); while(c>0) { n = cin.nextInt(); k = cin.nextInt(); int n2=2*n; int[] a=new int[n2]; for(int i=0;i<n2;i++) a[i]= cin.nextInt(); sortStr(a,k); c--; } } }(2)第二题:组成矩阵
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Problem Description:
对于一组水平线段及垂直线段,共有n条,已知其中必定存在一个若干条线段组成的矩形,输出这个矩形的左下角和右上角点,定义左下角点有最小的x值和y值,右上角点有最大的x值和y值。
线段可以重合,线段必须恰好能组成矩形,即线段不能头或尾部超出矩阵边界,如下图:
输入
第一行为线段数 n (4 <= n <= 25)
接下来有n行,每行为一条线段的首尾点坐标: xi yi xj yj
坐标值范围 -1e9 <= x, y <= 1e9
输出
在一行输出矩形的左下角点和右上角点坐标:
xi yi xj yj
根据定义有 xi < xj && yi < yj
样例输入
4
0 0 0 1
0 0 1 0
0 1 1 1
1 0 1 1
样例输出
0 0 1 1
二、思路:假如左下角的坐标是(x1,y1),右上角的坐标是(x2,y2)时,必然存在的输入数据如下
x1 y1 x1 y2
x1 y1 x2 y1
x1 y2 x2 y2
x2 y1 x2 y2
所以当判断两个坐标是否能够凑成一个矩阵的时候,只需判断输入数据中属否存在如上规律,如果存在,则这两个坐标分别是左下角坐标和右下角坐标,然后再用这两个坐标分别与最小的左下角坐标和最大的右上角坐标相比较;以此循环,最终能够得到最小的左下角坐标和最大的右上角坐标。
将输入的每行数据的前两个数作为左下角坐标集合,输入数据的每行的最后两个数做为右上角坐标集合,以此来两两判断左下角坐标集合与右上角坐标集合之间是否能够构成一个举证。
左下角集合和右上角集合中会存在着重复的数据,所以可以先去除重复的数据来减少时间的复杂度,我的思路是将已经出现过的坐标赋值为(-1,-1)
三、代码:
import java.util.*; public class Main { public static void minPos(int[][] arry) { int minX=0,minY=0; int maxX=0,maxY=0; int xi,yi,xj,yj; boolean bool; int[][] arry1=new int[arry.length][2]; int[][] arry2=new int[arry.length][2]; duplicateNum(arry1,arry2,arry); for(int i=0;i<arry.length;i++) { xi=arry1[i][0]; yi=arry1[i][1]; if(xi!=-1&&yi!=-1) { for (int j = 0; j < arry.length; j++) { xj = arry2[j][0]; yj = arry2[j][1]; if(xj!=-1&&yj!=-1) { bool = beIn(xi, yi, xj, yj, arry); if (bool) { if (minX >= xi && minY >= yi) { minX = xi; minY = yi; } if (maxX <= xj && maxY <= yj) { maxX = xj; maxY = yj; } } } } } } System.out.print(minX); System.out.print(minY); System.out.print(maxX); System.out.print(maxY); } public static void duplicateNum(int[][] arry1,int[][] arry2,int[][] arry) { for(int i=0;i<arry.length;i++ ) { arry1[i][0]=arry[i][0]; arry1[i][1]=arry[i][1]; arry2[i][0]=arry[i][2]; arry2[i][1]=arry[i][3]; } int x,y,x1,y1; for(int i=0;i<arry.length;i++) { x= arry1[i][0]; y=arry1[i][1]; x1= arry2[i][0]; y1= arry2[i][1]; for(int j=i+1;j<arry.length;j++) { if(x==arry1[j][0]&&y==arry1[j][1]) { arry1[j][0]=-1; arry1[j][1]=-1; } if(x1==arry2[j][0]&&y1==arry2[j][1]) { arry2[j][0]=-1; arry2[j][1]=-1; } } } } public static boolean beIn(int xi,int yi,int xj,int yj,int[][] arry) { int ai=xi,aj=yj; int bi=xj,bj=yi; int num=0; int flag1=0,flag2=0,flag3=0,flag4=0; for(int i=0;i<arry.length;i++) { int ci=arry[i][0],cj=arry[i][1]; int di=arry[i][2],dj=arry[i][3]; if(ci==xi&&cj==yi&&(di==ai&&dj==aj)&&flag1==0) { num++; flag1=1; continue; } if(ci==xi&&cj==yi&&(di==bi&&dj==bj)&&flag2==0) { num++; flag2=1; continue; } if(ci==ai&&cj==aj&&di==xj&&dj==yj&&flag3==0) { num++; flag3=1; continue; } if(ci==bi&&cj==bj&&di==xj&&dj==yj&&flag4==0) { num++; flag4=1; continue; } } if(num==4) return true; return false; } public static void main(String args[]) { Scanner cin = new Scanner(System.in); int c=cin.nextInt(); int[][] arry=new int[c][4]; for(int i=0;i<c;i++) for(int j=0;j<4;j++) { arry[i][j]= cin.nextInt(); } minPos(arry); } }(3)第三题:构造队列
一、题目:
构造队列
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Problem Description:
小明同学把1到n这n个数字按照一定的顺序放入了一个队列Q中。现在他对队列Q执行了如下程序:
while(!Q.empty()) //队列不空,执行循环
{
int x=Q.front(); //取出当前队头的值x
Q.pop(); //弹出当前队头
Q.push(x); //把x放入队尾
x=Q.front(); //取出这时候队头的值
printf("%d\n",x); //输出x
Q.pop(); //弹出这时候的队头
}
做取出队头的值操作的时候,并不弹出当前队头。
小明同学发现,这段程序恰好按顺序输出了1,2,3,...,n。现在小明想让你构造出原始的队列,你能做到吗?
输入
第一行一个整数T(T<=100)表示数据组数,每组数据输入一个数n(1<=n<=100000),输入的所有n之和不超过200000。
输出
对于每组数据,输出一行,表示原始的队列。数字之间用一个空格隔开,不要在行末输出多余的空格。
样例输入
4
1
2
5
10
样例输出
1
2 1
2 1 3
8 1 6 2 10 3 7 4 9 5
二、思路:根据题目的意思,可以理解为,
1)第一次从数组中的第二个数开始隔空取数字,一直到数组的末位;
2)当上次取的最后一个数恰好是数组的最后一个数的时候,这次从剩下的数组中也是按照1)中取数据的方式取数据,从第二个数据开始隔空取,一直到数组末位;
3)当上次取的最后一盒数是数组的倒数第二个数的时候,则在上次取剩下的数据的第一个数据开始隔空取数据;
4)当数组中的数据不为空时,依次循环上面的1)、2)或1)、3)
于此同时,用stackstatus记录每一轮取数据是从第一位数开始隔空取还是从第二位开始隔空取,如果是第一位开始,则status记为1,否则,记为0;
用stackNum来记录每一轮取出的数据的个数;
接下来是构造原始的数据队列,假设输出来的数组为que
1)从stackNum中弹出一个数num1,接着从que的末位去处num1个数一次存入int[num1] que1中,从stackStatus中弹出status1;
2)从stackNum中弹出一个数num2,接着从que的末位去处num2个数一次存入int[num2] que2中,从stackStatus中弹出status2;
3)int[num1+num2] que3,如果status2=1,则从que2开始,轮流从que2、que1中取出一个数存入que3;如果status2=0,则从que1开始,轮流从que1、que2中取出一个数存入que3;
4)que1=que3,num1=que3.length;
5)stackNum栈不为空时,一次重复上面的2)、3)、4)
最后得到的que1就是所要求的队列
三、代码
import java.util.*; public class Main { public static void statusAndNum(int n) { int n1=n; Stack<Integer> stackStatus=new Stack<Integer>(); Stack<Integer> stackNum=new Stack<Integer>(); int num=0 ; int k=0; int status=0; while(n>1) { if (k == n||k==0) { status = 0; stackStatus.push(status); n = n - num; num = 1; k = 2; while (k<n-1) { k += 2; num++; } stackNum.push(num); } else { status = 1; stackStatus.push(status); n = n - num; num = 1; k = 1; while (k < n-1) { k += 2; num++; } stackNum.push(num); } } queueOrder(stackStatus,stackNum,n1); } public static void queueOrder(Stack<Integer> stackStatus,Stack<Integer> stackNum,int n) { int[] que=new int[n]; for(int i=0;i<n;i++) { que[i]=i+1; } int num1,num2; num1=stackNum.pop(); int[] que1=new int[num1]; int i,j; int quePos=que.length-num1; for(i=que.length-num1,j=0;j<num1;i++,j++) { que1[j]=que[i]; } int status1=stackStatus.pop(); while(!stackNum.empty()) { num2=stackNum.pop(); int[] que2=new int[num2]; int[] que3=new int[num2+num1]; quePos=quePos-num2; for(i=quePos,j=0;j<num2;j++,i++) { que2[j]=que[i]; } int status2=stackStatus.pop(); if(status2>0) { int indexStr1=0,indexStr2=0,indexStr3=0; while(indexStr1<num1&&indexStr2<num2) { que3[indexStr3++] = que2[indexStr2]; que3[indexStr3++] = que1[indexStr1]; indexStr1++; indexStr2++; } if(indexStr1<num1) { que3[indexStr3++] = que1[indexStr1]; indexStr1++; } if(indexStr2<num2) { que3[indexStr3++] = que2[indexStr2]; indexStr2++; } } else { int indexStr1=0,indexStr2=0,indexStr3=0; while(indexStr1<num1&&indexStr2<num2) { que3[indexStr3++] = que1[indexStr1]; que3[indexStr3++] = que2[indexStr2]; indexStr1++; indexStr2++; } if(indexStr1<num1) { que3[indexStr3++] = que1[indexStr1]; indexStr1++; } if(indexStr2<num2) { que3[indexStr3++] = que2[indexStr2]; indexStr2++; } } que1=que3; num1=que1.length; } for(int reltI=0;reltI<n;reltI++) { System.out.print(que1[reltI]+" "); } } public static void main(String args[]) { Scanner cin = new Scanner(System.in); int c=cin.nextInt(); int num; while(c>0) { num=cin.nextInt(); statusAndNum(num); c--; } } }