7-1 子集和问题 (50分)
设集合S={x1,x2,…,xn}是一个正整数集合,c是一个正整数,子集和问题判定是否存在S的一个子集S1,使S1中的元素之和为c。试设计一个解子集和问题的回溯法。
输入格式:
输入数据第1行有2个正整数n和c,n表示S的大小,c是子集和的目标值。接下来的1行中,有n个正整数,表示集合S中的元素。 是子集和的目标值。接下来的1 行中,有n个正整数,表示集合S中的元素。
输出格式:
输出子集和问题的解,以空格分隔,最后一个输出的后面有空格。当问题无解时,输出“No Solution!”。
输入样例:
在这里给出一组输入。例如:
5 10
2 2 6 5 4
输出样例:
在这里给出相应的输出。例如:
2 2 6
思路:
一道老题就不多赘述了。主要说一下递归中如何强制跳出整个递归(详见代码)。
利用异常跳出整个递归。先写一个静态的StopMsgException类,再继承RuntimeException类,主函数调用递归部分直接而try,catch,注意catch里是StopMsgException e,最后递归里需要跳出的地方用throw StopMsgException抛出异常就ok了。
用return是无法跳出递归的,只会返回上一级!!!
怎么剪枝优化最后一个点都超时,比赛的时候给1s应该没问题,不必管他了。
dfs的else里的优化其实不用那么复杂,前四个个点如下简单优化也过了。。
x[loc]=1;
if(add+a[loc]<=c){
dfs(a,n,c,loc+1,x,add+a[loc]);
}
x[loc]=0;
if(add<c){
dfs(a,n,c,loc+1,x,add);
}
import java.util.ArrayList;
import java.util.Scanner;
public class 子集和问题 {
static class StopMsgException extends RuntimeException{
}
static Boolean flag=false;
static ArrayList<Integer> list=new ArrayList<Integer>();
static void dfs(int[] a,int n,int c,int loc,int[] x,int add){
if(loc>=n){//得到一个解
int sum=0;
for(int i=0;i<n;i++){
if(x[i]==1){
sum+=a[i];
list.add(a[i]);
}
}
if(sum==c){
flag=true;
for(int i:list){
System.out.print(i+" ");
}
throw new StopMsgException();
}
else{
list.clear();
}
}
else{
x[loc]=1;
if(add+a[loc]==c){
flag=true;
for(int i=0;i<n;i++){
if(x[i]==1){
System.out.print(a[i]+" ");
}
}
throw new StopMsgException();
}
if(add+a[loc]<c){
dfs(a,n,c,loc+1,x,add+a[loc]);
}
x[loc]=0;
if(add+a[loc]==c){
flag=true;
for(int i=0;i<n;i++){
if(x[i]==1){
System.out.print(a[i]+" ");
}
}
throw new StopMsgException();
}
if(add<c){
dfs(a,n,c,loc+1,x,add);
}
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int c=in.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=in.nextInt();
}
int[] x=new int[n];//元素只有1或0,用来记录元素有或者没有
try{
dfs(a,n,c,0,x,0);
}catch(StopMsgException e){
}
if(!flag){
System.out.println("No Solution!");
}
}
}
7-3 前序序列创建二叉树 (25分)
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以二叉链表存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,代表一棵空树。然后再对二叉树进行中序遍历,输出遍历结果。
输入格式:
多组测试数据,每组测试数据一行,该行只有一个字符串,长度不超过100。
输出格式:
对于每组数据,
输出二叉树的中序遍历的序列,每个字符后面都有一个空格。
每组输出一行,对应输入的一行字符串。
输入样例:(及其对应的二叉树)
abc##de#g##f###
输出样例:
c b e g d f a
虽然树的题平时没怎么做过,但是毕竟有数据结构的基础在,解题一看就懂了,不过这题得多敲,掌握如何构造树的结构,第一遍敲出了好多bug。
注意在中序遍历时if一定要包含全部的代码,以防出现空指针异常!
import java.util.Scanner;
public class 前序序列创建二叉树 {
static int i,length;
static String str;
static Node create(){//先序创建树
Node o=null;
if(i<length){
o=new Node();
o.mark=str.charAt(i);
i++;
if(o.mark!='#'){
o.left=create();
o.right=create();
}
}
return o;
}
static void midTraversal(Node o){//中序遍历输出
//此处必须全部包含起来要不有空指针异常,
//因为如果o没有左孩子或右孩子,那么函数传入的就是null
if(o.mark!='#'){
midTraversal(o.left);
System.out.print(o.mark+" ");
midTraversal(o.right);
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner in=new Scanner(System.in);
while(true){
i=0;
str=in.nextLine();
length=str.length();
Node root=create();
midTraversal(root);
System.out.println();
}
}
}
class Node{//定义结点结构
char mark;
Node left=null;
Node right=null;
}
7-4 电路布线 (20分)
在解决电路布线问题时,一种很常用的方法就是在布线区域叠上一个网格,该网格把布线区域划分成m*n个方格,布线时,转弯处必须采用直角,如已经有某条线路经过一个方格时,则在该方格上不允许叠加布线。如下图所示,如从一个方格a(2,1)的中心点到另一个方格b(8,8)的中心点布线时, 每个方格布线时需要1个单位的电路材料,所需要最少的电路材料是16。
输入格式:
第一行输入网格的m和n
第二行开始输入网格中已经布线的情况,如果已经有布线时,用1表示,尚未布线时,用0表示。
接下来两行分别输入需要布线的起始位置a和结束位置b。
输出格式:
输出从起始位置a到结束位置b布线时所需要的最少电路材料。
输入样例:
在这里给出一组输入。例如:
8 8
1 1 1 1 1 1 1 1
0 0 0 0 0 1 1 1
1 0 1 1 0 0 0 1
1 0 1 1 0 1 1 0
1 0 1 1 1 1 1 1
1 0 1 1 0 0 0 1
1 0 0 0 0 1 0 0
1 1 1 1 1 1 1 0
2 1
8 8
输出样例:
在这里给出相应的输出。例如:
16
思路:
又一道老题,就是个迷宫,用深搜找最短路径的话就得把每一条路都找出来,然后取其中最短的一条,即每找到一条路都得比较!注意static int shortest=m*n;是错误的写法,此时m,n还没有赋值,所以shortest这样写,初值就是0,得等到m,n复制之后,再将shortest=m*n;
import java.util.Scanner;
public class 电路布线 {
static int m,n;
static int shortest;
static void dfs(int[][] a,int i,int j,int e1,int e2,int count){
if(i==e1&&j==e2){//到达终点
// System.out.println(count);
if(shortest>count){
shortest=count;
}
}
else{
if(j-1>=0&&a[i][j-1]!=1){
a[i][j-1]=1;
dfs(a,i,j-1,e1,e2,count+1);
a[i][j-1]=0;
}
if(j+1<n&&a[i][j+1]!=1){
a[i][j+1]=1;
dfs(a,i,j+1,e1,e2,count+1);
a[i][j+1]=0;
}
if(i-1>=0&&a[i-1][j]!=1){
a[i-1][j]=1;
dfs(a,i-1,j,e1,e2,count+1);
a[i-1][j]=0;
}
if(i+1<m&&a[i+1][j]!=1){
a[i+1][j]=1;
dfs(a,i+1,j,e1,e2,count+1);
a[i+1][j]=0;
}
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner in=new Scanner(System.in);
m=in.nextInt();
n=in.nextInt();
shortest=m*n;
int metrix[][]=new int[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
metrix[i][j]=in.nextInt();
}
}
int start1=in.nextInt()-1;
int start2=in.nextInt()-1;
int end1=in.nextInt()-1;
int end2=in.nextInt()-1;
dfs(metrix,start1,start2,end1,end2,1);//起点就需要一单位的电线,所以置为1
System.out.println(shortest);
}
}
7-6 删除数组零元素 (10分)
从键盘读入n个整数放入数组中,编写函数CompactIntegers,删除数组中所有值为0的元素,其后元素向数组首端移动。注意,CompactIntegers函数需要接受数组及其元素个数作为参数,函数返回值应为删除操作执行后数组的新元素个数。输出删除后数组中元素的个数并依次输出数组元素。
输入格式:
输入格式说明:5为输入数据的个数,3 4 0 0 2 是以空格隔开的5个整数
输出格式:
请3为非零数据的个数,3 4 2 是以空格隔开的3个非零整数
输入样例:
在这里给出一组输入。例如:
5
3 4 0 0 2
输出样例:
在这里给出相应的输出。例如:
3
3 4 2
思路:
一道基础小题,用来加强优化时间的意识还不错。
import java.util.*;
public class 删除数组零元素 {
static int count=0;
static int CompactIntegers(int[] a,int n){
for(int i=0;i<n-count;i++){//遍历长度要随之更改,优化时间
if(a[i]==0){
count++;
for(int j=i;j<n-1;j++){
a[j]=a[j+1];
}
i--;//防止下一个0前进到了此位置
}
}
return n-count;
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=in.nextInt();
}
int L=CompactIntegers(a,n);
System.out.println(L);
for(int i=0;i<L-1;i++){
System.out.print(a[i]+" ");
}
System.out.println(a[L-1]);
}
}
7-8 最大最小公倍数 (10分)
已知一个正整数N,问从1~N中任选出三个数,他们的最小公倍数最大可以为多少。
数据规模与约定
1 <= N <= 106。
输入格式:
输入一个正整数N。
输出格式:
输出一个整数,表示你找到的最小公倍数。
输入样例:
在这里给出一组输入。例如:
9
输出样例:
在这里给出相应的输出。例如:
504
思路:找子集,然后计算数量为三的子集的最小公倍数(辗转相除法求最大公约数,a,b的最小公倍数=a*b/(a,b的最大公约数),然后再让求出来的那个最小公倍数和第三个数求最小公倍数),每次比较最后找最大的就可以了,不知道为啥样例过了,但测试点全错?????什么鬼
import java.util.Scanner;
public class 最大最小公倍数 {
static int end=0;
static int N;
static int[] a;
//求三个数的最小公倍数
static int fun(int[] a){
int b=a[0],c=a[1],d=a[2];
while(a[1]!=0){
int temp=a[0]%a[1];
a[0]=a[1];
a[1]=temp;
}
//此时a[0]为最大公约数
int sb=b*c/a[0];//a[0]和a[1]的最小公倍数
int ssb=sb;
while(a[2]!=0){
int temp=sb%a[2];
sb=a[2];
a[2]=temp;
}
//此时sb为最大公约数
int result=ssb*d/sb;
return result;
}
//子集
static void dfs(int[] x,int n,int i){
if(i>n){//一个解,>n是对于数组长度为n+1的情况
int count=0;
a=new int[n+1];//重新清零
for(int j=1;j<=N;j++){
if(x[j]==1){
a[count]=j;
count++;
}
}
if(count==3){//任意选出三个
int result=fun(a);
if(end<result){
end=result;
}
}
}
else{
x[i]=0;
dfs(x,n,i+1);
x[i]=1;
dfs(x,n,i+1);
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner in=new Scanner(System.in);
N=in.nextInt();
int[] x=new int[N+1];
dfs(x,N,1);
System.out.println(end);
}
}
7-9 猴子分苹果 (10分)
秋天到了,n只猴子采摘了一大堆苹果放到山洞里,约定第二天平分。这些猴子很崇拜猴王孙悟空,所以都想给他留一些苹果。第一只猴子悄悄来到山洞,把苹果平均分成n份,把剩下的m个苹果吃了,然后藏起来一份,最后把剩下的苹果重新合在一起。这些猴子依次悄悄来到山洞,都做同样的操作,恰好每次都剩下了m个苹果。第二天,这些猴子来到山洞,把剩下的苹果分成n分,巧了,还是剩下了m个。问,原来这些猴子至少采了多少个苹果。
输入格式:
两个整数,n m
输出格式:
一个整数,表示原来苹果的数目
输入样例:
在这里给出一组输入。例如:
5 1
输出样例:
在这里给出相应的输出。例如:
15621
思路:
小模拟,直接暴力,五个点,第一个点错,后俩超时。。
import java.util.Scanner;
public class 猴子分苹果 {
static int n,m;
static Boolean fun(int x){
for(int i=0;i<n;i++){//第一天每只猴子的操作
x-=m;
if(x%n!=0){
return false;
}
x-=(x/n);
}
//第二天
if(x>n&&x%n==m){
return true;
}
return false;
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner in=new Scanner(System.in);
n=in.nextInt();
m=in.nextInt();
if(n==0){
System.out.println(0);
}
for(int i=n;;i++){
if(fun(i)){
System.out.println(i);
return;
}
}
}
}
7-10 你究竟有几个好妹妹 (25分)
小李的QQ列表里有很多联系人,他修改了所有人的备注姓名。其中有些联系人名字的前部或后部被加了诸如"好妹妹"的字样。请你打印出小李的所有的女性"好妹妹"。打印时请按照联系人原始名字升序排列,如果两人名字相同则年龄小的排在前面;如果两人年龄相同,则较早成为好友的排在前面。题目保证好妹妹标志不会同时出现在前缀和后缀,同一天里也不会添加名字相同并且年龄相同的"好妹妹"。
输入格式:
第一行给出标识好妹妹标志的字符串,该字符串内不包含空格,最大长度为6并且不区分大小写。第二行给出QQ列表的所有联系人个数n (1≤ n ≤105),随后n行列出所有联系人信息,每行信息格式如下:
备注姓名 性别 年龄 结交好友日期
其中备注姓名不超过30个字符;性别用字符表示,F表示女性,M代表男性;结交好友日期采用诸如"2001.01.01"的格式表示。
输出格式:
第一行输出好妹妹的数目k,随后k行打印出去掉好妹妹标志的名字、年龄以及结交好友日期。
输入样例:
例如:
hmm
10
Zoehmm F 28 2001.01.01
hmmBeith F 18 2010.10.21
Zack M 35 1999.09.18
hmmAdam F 21 2010.10.21
Beithhmm F 21 2010.10.21
Chelse F 45 2005.12.03
DaisyHMM F 30 2008.08.05
Eston M 18 2015.03.04
hmmFrany F 8 2018.07.15
JackHM F 7 2017.09.11
输出样例:
输出:
6
Adam 21 2010.10.21
Beith 18 2010.10.21
Beith 21 2010.10.21
Daisy 30 2008.08.05
Frany 8 2018.07.15
Zoe 28 2001.01.01
思路:
类排序(继承Comparable接口)的一道题,没多大意思。
注意三点:
1.“好妹妹”标识不区分大小写,就把表示变成全小写,与备注姓名比较时也将相应字段变成小写方便比较。
2.将日期分割以‘.’时,要加转义字符\\
3.按原始姓名升序排列时,利用字符串的compareTo函数即可,
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class 类排序_你究竟有几个好妹妹 {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner in=new Scanner(System.in);
String mark=in.nextLine().toLowerCase();//不区分大小写,全变成小写
int n=in.nextInt();
Sister sis[]=new Sister[n];
ArrayList<Sister> list=new ArrayList<Sister>();//存放好妹妹
for(int i=0;i<n;i++){
sis[i]=new Sister();
sis[i].remark=in.next();
sis[i].sex=in.next();
if(sis[i].sex.equals("F")){//只有女的才有可能成为好妹妹
String front=sis[i].remark.substring(0, 3).toLowerCase();
String behind=sis[i].remark.substring(sis[i].remark.length()-3).toLowerCase();
if(front.equals(mark)){
sis[i].remark=sis[i].remark.substring(3);
list.add(sis[i]);
}
if(behind.equals(mark)){
sis[i].remark=sis[i].remark.substring(0, sis[i].remark.length()-3);
list.add(sis[i]);
}
}
sis[i].age=in.nextInt();
sis[i].date=in.next();
String[] str=sis[i].date.split("\\.");//'.'需要转义
sis[i].y=Integer.parseInt(str[0]);
sis[i].m=Integer.parseInt(str[1]);
sis[i].d=Integer.parseInt(str[2]);
//System.out.println(sis[i].remark+" "+sis[i].sex+" "+sis[i].age+" "+sis[i].date);
}
Sister hmm[]=new Sister[list.size()];
for(int i=0;i<list.size();i++){
hmm[i]=list.get(i);
}
Arrays.sort(hmm);
System.out.println(hmm.length);
for(Sister s:hmm){
System.out.println(s.remark+" "+s.age+" "+s.date);
}
}
}
class Sister implements Comparable<Sister>{
String remark;
String sex;
int age;
String date;
int y,m,d;
@Override
public int compareTo(Sister o) {
// TODO 自动生成的方法存根
if(this.remark.compareTo(o.remark)>0){
return 1;
}
else if(this.remark.compareTo(o.remark)<0){
return -1;
}
else{//姓名一样
if(this.age>o.age){
return 1;
}
else if(this.age<o.age){
return -1;
}
else{//年龄一样
if(this.y>o.y){
return 1;
}
else if(this.y<o.y){
return -1;
}
else{
if(this.m>o.m){
return 1;
}
else if(this.m<o.m){
return -1;
}
else{
if(this.d>o.d){
return 1;
}
else if(this.d<o.d){
return -1;
}
else{
return 0;
}
}
}
}
}
}
}