第一题
题目
问题描述
在计算机存储中,12.5MB是多少字节?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
,常见的有:
bit(b,位),byte(B,字节),Kilo Byte(KB)
Mega Byte(MB),Giga Byte(GB)
Tera Byte(TB)
只有1B=8b,其他的都是1024进制
所以:
12.5MB=12.5*1024*1024B
13107200
第二题
题目
问题描述
由1对括号,可以组成一种合法括号序列:()。
由2对括号,可以组成两种合法括号序列:()()、(())。
由4对括号组成的合法括号序列一共有多少种?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
14
package competition2;
public class Main7OfMy
{
/*
* 这是一个递归的问题,每一个位置都有两种选择,要么是左括号
* 要么是右括号,但是放括号有两个条件约束:
* 1.左右括号剩下的数量都大于0才可以继续放括号,
* 2.要保证放下的括号后,左括号数量永远要大于或等于右括号的数量
*/
public static void main(String[] args)
{
System.out.println(Choice(4, 4, 4));
}
static public int Choice(int number, int l, int r)
{
int count = 0;
if (l == 0 && r == 0)
{
return 1;
}
if (l > 0 && (number - (l - 1) >= number - r))
{
count += Choice(number, l - 1, r);
}
if (r > 0 && (number - l >= number - (r - 1)))
{
count += Choice(number, l, r - 1);
}
return count;
}
}
第三题
题目
问题描述
一个包含有2019个结点的有向图,最多包含多少条边?(不允许有重边)
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
4074342
有2019个结点,每两个就能组成一个有来有回的组合,那么最多就是Cn2=n(n-1)/(2*1)
那么就是2019*2018/2=4074342
第四题
题目
问题描述
将LANQIAO中的字母重新排列,可以得到不同的单词,
如LANQIAO、AAILNOQ等,注意这7个字母都要被用上,单词不一定有具体的英文意义。
请问,总共能排列如多少个不同的单词。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
2520
这里说四种思路,第一种:这个是全排列的问题,而且刚好发现里面只有A是出现了两次,
其他的都是只出现一次,那么首先不考虑重复出现的字母,全排列的次数是7*6*5*4*3*2*1=5040
但是因为有两个A是一样的,所以有一半是重复的,所以结果是5040/2=2520
package competition2;
import java.util.HashSet;
public class Main8OfMy_1
{
/*
* 先说一下思路吧,就是将所有的全排列加入到这个set集合中,然后集合的大小就是个数
* 全排列的思路可以看我的博客有写
*/
public static HashSet<String> set=new HashSet<String>();
public static char[] arr="LANQIAO".toCharArray();
public static char temp;
public static String TempString="";
public static void main(String[] args)
{
kinds(arr, 0, arr.length, set);
System.out.println(set.size());
}
public static void kinds(char[] arr,int start,int end,HashSet<String> set)
{
if(start==end)
{
for (char element : arr)
{
TempString+=element;
}
set.add(TempString);
TempString="";
}
for (int i = start; i < end; i++)
{
swap(arr, i, start);
kinds(arr, start+1, end, set);
swap(arr, i, start);
}
}
public static void swap(char[] arr,int x,int y)
{
temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
}
package competition2;
import java.util.HashSet;
public class Main8OfMy_2
{
/*
* 这个也是全排列的思想,就是将上面的位置交换改成了
* 一个数组来标识和一个临时数组来保存
*/
private static char[] arr="LANQIAO".toCharArray();
private static Integer length=arr.length;
private static boolean[] visit=new boolean[length];
private static HashSet<String> set=new HashSet<String>();
private static char[] temp=new char[length];
public static void main(String[] args)
{
kinds(0);
System.out.println(set.size());
}
public static void kinds(int index)
{
if(index==length)
{
set.add(new String(temp));
return;
}
for(int x=0;x<length;x++)
{
if(!visit[x])
{
temp[index]=arr[x];
visit[x]=true;
kinds(index+1);
visit[x]=false;
}
}
}
}
package competition2;
import java.util.HashSet;
public class Main8OfMy_3
{
/*
* 现在这种思路就是写一个去重复的全排列,
* 就是在进行交换之前,那当前位置的元素和要交换元素之间的每一个元素进行比较
* 如果发现相同的就跳过当前的递归,因为如果出现了第二个相同的元素再继续递归
* 就会得到重复的结果了,这个元素第一次出现进行递归的时候已经将后面的所有可能都
* 递归出来了
*/
public static HashSet<String> set=new HashSet<String>();
public static char[] arr="LANQIAO".toCharArray();
public static char temp;
public static String TempString="";
public static void main(String[] args)
{
kinds(arr, 0, arr.length, set);
System.out.println(set.size());
}
public static void kinds(char[] arr,int start,int end,HashSet<String> set)
{
if(start==end)
{
for (char element : arr)
{
TempString+=element;
}
set.add(TempString);
TempString="";
}
for (int i = start; i < end; i++)
{
if(IfExit(arr, start, i))
{
continue;
}
swap(arr, i, start);
kinds(arr, start+1, end, set);
swap(arr, i, start);
}
}
public static boolean IfExit(char[] arr,int x,int y)
{
for(;x<y;x++)
{
if(arr[x]==arr[y])
{
return true;
}
}
return false;
}
public static void swap(char[] arr,int x,int y)
{
temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
}
第题五
题目
问题描述
给定一个单词,请使用凯撒密码将这个单词加密。
凯撒密码是一种替换加密的技术,单词中的所有字母都在字母表上向后偏移3位后被替换成密文。
即a变为d,b变为e,...,w变为z,x变为a,y变为b,z变为c。
例如,lanqiao会变成odqtldr。
输入格式
输入一行,包含一个单词,单词中只包含小写英文字母。
输出格式
输出一行,表示加密后的密文。
样例输入
lanqiao
样例输出
odqtldr
评测用例规模与约定
对于所有评测用例,单词中的字母个数不超过100。
答案
package competition2;
import java.util.Scanner;
public class Main1OfMy
{
public static void main(String[] args)
{
Scanner in=new Scanner(System.in);
String nextLine = in.nextLine();
char[] temp=new char[nextLine.length()];
for (int i = 0; i < nextLine.length(); i++)
{
temp[i]=judge(nextLine.charAt(i));
}
System.out.println(new String(temp));
in.close();
}
public static char judge(char in)
{
if(in<'x')
{
return (char)(in+3);
}
if(in=='x')
{
return 'a';
}
if(in=='y')
{
return 'b';
}
else
{
return 'c';
}
}
}
第六题
题目
问题描述
给定三个整数 a, b, c,如果一个整数既不是 a 的整数倍
也不是 b 的整数倍还不是 c 的整数倍,则这个数称为反倍数。
请问在 1 至 n 中有多少个反倍数。
输入格式
输入的第一行包含一个整数 n。
第二行包含三个整数 a, b, c,相邻两个数之间用一个空格分隔。
输出格式
输出一行包含一个整数,表示答案。
样例输入
30
2 3 6
样例输出
10
样例说明
以下这些数满足要求:1, 5, 7, 11, 13, 17, 19, 23, 25, 29。
评测用例规模与约定
对于 40% 的评测用例,1 <= n <= 10000。
对于 80% 的评测用例,1 <= n <= 100000。
对于所有评测用例,1 <= n <= 1000000,1 <= a <= n,1 <= b <= n,1 <= c <=
答案
package competition2;
import java.util.Scanner;
public class Main2
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int n=in.nextInt();
int a,b,c;
a=in.nextInt();b=in.nextInt();c=in.nextInt();
in.close();
int count=0;
for(int x=1;x<=n;x++)
{
// if(x%a!=0&&x%b!=0&&x%c!=0)
// {
// count++;
// }
if(x%a==0)
{
continue;
}
if(x%b==0)
{
continue;
}
if(x%c==0)
{
continue;
}
count++;
}
System.out.println(count);
}
}
第七题
题目
问题描述
对于一个 n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,
我们称填好的表格为一个螺旋矩阵。
例如,一个 4 行 5 列的螺旋矩阵如下:
1 2 3 4 5
14 15 16 17 6
13 20 19 18 7
12 11 10 9 8
输入格式
输入的第一行包含两个整数 n, m,分别表示螺旋矩阵的行数和列数。
第二行包含两个整数 r, c,表示要求的行号和列号。
输出格式
输出一个整数,表示螺旋矩阵中第 r 行第 c 列的元素的值。
样例输入
4 5
2 2
样例输出
15
评测用例规模与约定
对于 30% 的评测用例,2 <= n, m <= 20。
对于 70% 的评测用例,2 <= n, m <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= r <= n,1 <= c <=
答案
package competition2;
import java.util.Scanner;
public class Main3OfMy_1
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int r = sc.nextInt();
int c = sc.nextInt();
int[][] arr=new int[n][m];
sc.close();
int num=1;
int up=0,down=n,right=m,left=0;
while(true)
{
for(int col=left;col<right;col++)
{
arr[up][col]=num++;
}
up++;
if(up==down)
{
break;
}
for(int row=up;row<down;row++)
{
arr[row][right-1]=num++;
}
right--;
if(right==left)
{
break;
}
for(int col=right-1;col>=left;col--)
{
arr[down-1][col]=num++;
}
down--;
if(down==up)
{
break;
}
for(int row=down-1;row>=up;row--)
{
arr[row][left]=num++;
}
left++;
if(left==right)
{
break;
}
}
System.out.println(arr[r-1][c-1]);
}
}
第八题
题目
问题描述
如果一个序列的奇数项都比前一项大,偶数项都比前一项小,
则称为一个摆动序列。即 a[2i]<a[2i-1], a[2i+1]>a[2i]。
小明想知道,长度为 m,每个数都是 1 到 n 之间的正整数的摆动序列一共有多少个。
输入格式
输入一行包含两个整数 m,n。
输出格式
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
样例输入
3 4
样例输出
14
样例说明
以下是符合要求的摆动序列:
2 1 2
2 1 3
2 1 4
3 1 2
3 1 3
3 1 4
3 2 3
3 2 4
4 1 2
4 1 3
4 1 4
4 2 3
4 2 4
4 3 4
评测用例规模与约定
对于 20% 的评测用例,1 <= n, m <= 5;
对于 50% 的评测用例,1 <= n, m <= 10;
对于 80% 的评测用例,1 <= n, m <= 100;
对于所有评测用例,1 <= n, m <= 1000。
答案
递归
package competition2;
import java.util.Scanner;
public class Main4
{
/*
* 首先你可能想到的是完全的递归,但是这样子递归算法复杂度接近指数级别的
* 看看评测用例规模与约定的数据规模,完全是不够用的
*/
public static int m,n,count=0;
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
m=in.nextInt();
n=in.nextInt();
in.close();
for(int x=2;x<=n;x++)
{
start(2, x, 1);
}
System.out.println(count);
}
/**
* @param index 代表当前的位置,可以用来判断当前位置的奇偶性
* @param before 代表当前位置的前一个数是什么
* @param now 代表当前位置的这个数是什么
*/
public static void start(int index,int before,int now)
{
if(index==m)
{
count=(count+1)%10000;
}
if(index%2==0)
{
if(now+1<before)
{
start(index, before, now+1);
}
if(index<m)
{
start(index+1, now, now+1);
}
}
else
{
if(now<n)
{
start(index, before, now+1);
}
if(index<m)
{
start(index+1, now, 1);
}
}
}
}
优化
package competition2;
import java.util.Scanner;
public class Main4OfMy_1
{
/*
* 首先还是一样,第一个位置可以选择的是2-n
* 到后面的规律就是:
* 如果是奇数位置选择了i,下一个是偶数位置,可以选择的是1到i-1
* 如果是偶数位置选择了i,下一个是奇数位置,可以选择的是i+1到n
* 而且像上面的递归包含了大量的重复的子问题,
* 所以可以使用记忆型的递归模型
* 递归式子是:
* dfs(i,k)=求和{dfs(temp,k+1)} k是奇数,temp是1到i-1
* dfs(i,k)=求和{dfs(temp,k+1)} k是偶数,temp是i+1到n
* 对于 80% 的评测用例,1 <= n, m <= 100;
* 但是这样算法复杂度是接近O(n^3),所以只能通过80%的数据
*/
public static int[][] arr=new int[1000][1000];
public static int m,n,mod=10000;
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
m=sc.nextInt();
n=sc.nextInt();
int count=0;
for(int x=2;x<=n;x++)
{
count =(count+dfs(x, 1))%mod;
}
System.out.println(count);
}
public static int dfs(int i,int k)
{
if(k==m)
{
return 1;
}
if(arr[i][k]!=0)
{
return arr[i][k];
}
//如果是奇数的话
if((k&1)==1)
{
for(int x =1;x<i;x++)
{
arr[i][k]=(arr[i][k]+dfs(x, k+1))%mod;
}
}
else
{
for(int x=i+1;x<=n;x++)
{
arr[i][k]=(arr[i][k]+dfs(x, k+1))%mod;
}
}
return arr[i][k];
}
}
优化
package competition2;
import java.util.Scanner;
public class Main4OfMy_2
{
/*
* 又来写老套路了,上面的算法有百分之二十的数据是无法通过的,那如何是好呀?
* 这种需要递归的,而且又有很多重复的子问题的,可以进行记忆型递归
* 还有一种更加优化的思路:这种递归中有加总的样子,可以修改递归式子来进行优化
* 想尽办法将递归式子变成那种集合的意义.
*
* 上面中dfs(i,k)的意义是第k个位置填写i的时候有多少种可能
* 现在改编成集合的意义,
* dfs(i,k)的意义是:
* 当k是奇数的时候,第k个位置中可以填写i+1到n,填写i+1到n一共有多少种可能
* 当k是偶数的时候,第k个位置中可以填写1到i-1,填写1到i-1一共有多少种可能
* 那么对应的递归方程式子如何进行拆分呢?
* k是奇数的时候:
* dfs(i,k)=dfs(i-1,k+1)+dfs(i+1,k)
* k是偶数的时候:
* dfs(i,k)=dfs(i+1,k+1)+dfs(i-1,k)
* 那么是如何意思呢?
* 比如题目给出的例子,m=3,n=4
* dfs(2,1)表示第一个位置可以填2,3,4,
* dfs(2,1)=dfs(3,1)+dfs(1,2)
* dfs(3,1)=dfs(4,1)+dfs(2,2)
* dfs(1,2)=dfs(0,2)+dfs(2,3)
* ......对应下面的
* dfs(2,1)=dfs(3,1)+dfs(1,2)分成两部分
* 2 1 ...
* 和
* 3 1 ...
* 3 2 ...
* 4 1 ...
* 4 2 ...
* 4 3 ...
* 就是将开头是2的和开头不是2的两部分
*/
public static int m,n,mod=10000;
public static int[][] arr=new int[1000][1000];
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
m=sc.nextInt();
n=sc.nextInt();
sc.close();
int count = dfs(2, 1);
System.out.println(count);
}
public static int dfs(int i,int k)
{
if(i<1 ||i>n)
{
return 0;
}
if(k==m)
{
if((k&1)==1)
{
arr[i][k]=n-i+1;
}
else
{
arr[i][k]=i;
}
}
if(arr[i][k]!=0)
{
return arr[i][k];
}
if((k&1)==1)
{
arr[i][k]=(arr[i][k]+dfs(i+1, k)+dfs(i-1, k+1))%mod;
}
else
{
arr[i][k]=(arr[i][k]+dfs(i-1, k)+dfs(i+1, k+1))%mod;
}
return arr[i][k];
}
}
最后优化
package competition2;
import java.util.Scanner;
public class Main4OfMy_3
{
/*
* 上面的那个优化后的递归的方法,因为递归的层次太深了,会超出栈空间的
* 所以我们得继续优化,将递归改成递推,而递推就是递归的逆过程
* 比如我们看上面递归的函数的出口,从而初始化递推的数组,
* 然后按照递归的逆过程来生成递推的数组
*
* 递归的出口:
* if(k==m)
{
if((k&1)==1)
{
arr[i][k]=n-i+1;
}
else
{
arr[i][k]=i;
}
}
对应的递推的初始化递推数组:
for(int x=n;x>0;x--)
{
if((m&1)==1)
{
arr[x][m]=n-x+1;
}
else
{
arr[x][m]=x;
}
}
然后是递归的逆过程进行递推,奇数列就逆着递推,偶数列就顺着递推
为什么呢?
因为奇数列递推需要这个列的靠后一个数据
而偶数列递推需要靠前的一共数据
*/
public static int m,n;
public static int[][] arr;
public static int mod=10000;
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
m=sc.nextInt();
n=sc.nextInt();
arr=new int[n+1][m+1];
sc.close();
//初始化数组
for(int x=n;x>0;x--)
{
if((m&1)==1)
{
arr[x][m]=n-x+1;
}
else
{
arr[x][m]=x;
}
}
//然后进行递归的逆过程
for(int y=m-1;y>0;y--)
{
//如果是奇数
if((y&1)==1)
{
for(int x=n;x>0;x--)
{
arr[x][y]=((x==n ? 0: arr[x+1][y])+arr[x-1][y+1])%mod;
}
}
else
{
for(int x=1;x<=n;x++)
{
arr[x][y]=(arr[x-1][y]+(x==n?0:arr[x+1][y+1]))%mod;
}
}
}
System.out.println(arr[2][1]);
// 输出数组看一下
// for(int x=0;x<5;x++)
// {
// for(int y=0;y<4;y++)
// {
// System.out.print(arr[x][y]+" ");
// }
// System.out.println();
// }
}
}
第九题
题目
问题描述
小明和朋友们一起去郊外植树,他们带了一些在自己实验室精心研究出的小树苗。
小明和朋友们一共有 n 个人,他们经过精心挑选,在一块空地上每个人挑选了一个适合植树的位置,
总共 n 个。他们准备把自己带的树苗都植下去。
然而,他们遇到了一个困难:有的树苗比较大,而有的位置挨太近,导致两棵树植下去后会撞在一起。
他们将树看成一个圆,圆心在他们找的位置上。如果两棵树对应的圆相交,
这两棵树就不适合同时植下(相切不受影响),称为两棵树冲突。
小明和朋友们决定先合计合计,只将其中的一部分树植下去,保证没有互相冲突的树。
他们同时希望这些树所能覆盖的面积和(圆面积和)最大。
输入格式
输入的第一行包含一个整数 n ,表示人数,即准备植树的位置数。
接下来 n 行,每行三个整数 x, y, r,表示一棵树在空地上的横、纵坐标和半径。
输出格式
输出一行包含一个整数,表示在不冲突下可以植树的面积和。
由于每棵树的面积都是圆周率的整数倍,请输出答案除以圆周率后的值(应当是一个整数)。
样例输入
6
1 1 2
1 4 2
1 7 2
4 1 2
4 4 2
4 7 2
样例输出
12
评测用例规模与约定
对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 20;
对于所有评测用例,1 <= n <= 30,0 <= x, y <= 1000,1 <= r <= 1000。
答案
package competition2;
import java.util.Scanner;
public class Main5OfMy_1
{
/*
* 每棵树可以选也可以不选,但不知道哪种决策结果最大,
* 只能先考虑暴力搜索每种情况,总可选数为2的n次方的深度优先搜索。
* 下面area表示树占用的面积,遍历每一种情况,将最大的面积赋值给area
* trees是保存树的数组,arr是保存树和树之间有没有冲突的情况
*/
public static int area=0;
public static int n;
public static MyTree[] trees;
public static int [][] arr;
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
n=in.nextInt();
trees=new MyTree[n];
for(int x=0;x<n;x++)
{
trees[x]=new MyTree(in.nextInt(), in.nextInt(), in.nextInt());
}
arr=new int[n][n];
for(int x=0;x<n-1;x++)
{
for(int y=x+1;y<n;y++)
{
if(trees[x].IsConflict(trees[y]))
{
arr[x][y]=1;
arr[y][x]=1;
}
}
}
//上面都是将数组准备好
dfs(0, 0);
System.out.println(area);
in.close();
}
//这里index代表想加入第几棵树,sum代表加入到index-1棵树最大的占用面积是多少
public static void dfs(int sum,int index)
{
//当遍历到最后一棵树后就直接计算总面积
if(index==n)
{
area=sum>area?sum:area;
return;
}
//先看下index这棵树能不能种下去
boolean IsOk=true;
for(int x=0;x<n;x++)
{
if(trees[x].Isplant && arr[x][index]==1)
{
IsOk=false;
break;
}
}
//情况1:如果可以种下去就选择这棵树
if(IsOk)
{
trees[index].Isplant=true;
int rr=trees[index].r*trees[index].r;
dfs(sum+rr, index+1);
trees[index].Isplant=false; //回溯
}
//情况2:不选这棵树
trees[index].Isplant=false;
dfs(sum, index+1);
}
}
class MyTree
{
int x;
int y;
int r;
boolean Isplant;
public MyTree(int x, int y, int r)
{
this.x = x;
this.y = y;
this.r = r;
}
public boolean IsConflict(MyTree myTree)
{
if((x-myTree.x)*(x-myTree.x)+(y-myTree.y)*(y-myTree.y)<(r+myTree.r)*(r+myTree.r))
{
return true;
}
return false;
}
}
优化
package competition2;
import java.util.Arrays;
import java.util.Scanner;
public class Main5OfMy_2
{
/*
* 优化的关键点在于用类似贪心的办法(但不是贪心):
* 将圆按半径从大到小排序,这样优先考虑半径大的圆的选与不选问题;
* 另外把“选”这个分支放在“不选”这个分支前面执行,这样我们相信会尽早地遇到最优解。
*
* 基于这个假设,在递归之前我们可以以O(N)的复杂度存储所有圆的“半径的平方”的后缀和,
* 计为数组s;在递归函数dfs中,参数sum代表index之前的选择策略所得到的sum,
* s[index]代表包括index索引及之后续所有圆的半径的平方和,
* 如果sum+s[index]小于等于已经求得的ans,那就不必进行任何后续的选择试探了,可立即退出递归。
*/
/*
* 每棵树可以选也可以不选,但不知道哪种决策结果最大,
* 只能先考虑暴力搜索每种情况,总可选数为2的n次方的深度优先搜索。
* 下面area表示树占用的面积,遍历每一种情况,将最大的面积赋值给area
* trees是保存树的数组,arr是保存树和树之间有没有冲突的情况
*/
public static int area=0;
public static int n;
public static MyTree1[] trees;
public static int [][] arr;
//半径平方后缀和
public static int[] suffix;
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
n=in.nextInt();
trees=new MyTree1[n];
for(int x=0;x<n;x++)
{
trees[x]=new MyTree1(in.nextInt(), in.nextInt(), in.nextInt());
}
arr=new int[n][n];
for(int x=0;x<n-1;x++)
{
for(int y=x+1;y<n;y++)
{
if(trees[x].IsConflict(trees[y]))
{
arr[x][y]=1;
arr[y][x]=1;
}
}
}
//初始化后缀和排序树数组
Arrays.sort(trees);
suffix=new int[n];
suffix[n-1]=trees[n-1].r*trees[n-1].r;
for(int x=n-1-1;x>=0;x--)
{
suffix[x]=suffix[x+1]+trees[x].r*trees[x].r;
}
//上面都是将数组准备好
dfs(0, 0);
System.out.println(area);
in.close();
}
//这里index代表想加入第几棵树,sum代表加入到index-1棵树最大的占用面积是多少
public static void dfs(int sum,int index)
{
//当遍历到最后一棵树后就直接计算总面积
if(index==n)
{
area=sum>area?sum:area;
return;
}
//修改的地方来了,如果index之前的sum加上自index开始的半径的平方和小于ans,则没必要继续
if(sum+suffix[index]<=area)
{
return;
}
//先看下index这棵树能不能种下去
boolean IsOk=true;
for(int x=0;x<n;x++)
{
if(trees[x].Isplant && arr[x][index]==1)
{
IsOk=false;
break;
}
}
//情况1:如果可以种下去就选择这棵树
if(IsOk)
{
trees[index].Isplant=true;
int rr=trees[index].r*trees[index].r;
dfs(sum+rr, index+1);
trees[index].Isplant=false; //回溯
}
//情况2:不选这棵树
trees[index].Isplant=false;
dfs(sum, index+1);
}
}
class MyTree1 implements Comparable<MyTree1>
{
int x;
int y;
int r;
boolean Isplant;
public MyTree1(int x, int y, int r)
{
this.x = x;
this.y = y;
this.r = r;
}
public boolean IsConflict(MyTree1 MyTree1)
{
if((x-MyTree1.x)*(x-MyTree1.x)+(y-MyTree1.y)*(y-MyTree1.y)<(r+MyTree1.r)*(r+MyTree1.r))
{
return true;
}
return false;
}
@Override
public int compareTo(MyTree1 o)
{
return o.r-this.r;//注意这里是要降序的
}
}
第十题
题目
问题描述
2015年,全中国实现了户户通电。作为一名电力建设者,小明正在帮助一带一路上的国家通电。
这一次,小明要帮助 n 个村庄通电,其中 1 号村庄正好可以建立一个发电站,所发的电足够所有村庄使用。
现在,这 n 个村庄之间都没有电线相连,小明主要要做的是架设电线连接这些村庄,
使得所有村庄都直接或间接的与发电站相通。
小明测量了所有村庄的位置(坐标)和高度,如果要连接两个村庄,
小明需要花费两个村庄之间的坐标距离加上高度差的平方,
形式化描述为坐标为 (x_1, y_1) 高度为 h_1 的村庄与坐标为 (x_2, y_2)
高度为 h_2 的村庄之间连接的费用为
sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2))+(h_1-h_2)*(h_1-h_2)。
在上式中 sqrt 表示取括号内的平方根。请注意括号的位置,高度的计算方式与横纵坐标的计算方式不同。
由于经费有限,请帮助小明计算他至少要花费多少费用才能使这 n 个村庄都通电。
输入格式
输入的第一行包含一个整数 n ,表示村庄的数量。
接下来 n 行,每个三个整数 x, y, h,分别表示一个村庄的横、纵坐标和高度,
其中第一个村庄可以建立发电站。
输出格式
输出一行,包含一个实数,四舍五入保留 2 位小数,表示答案。
样例输入
4
1 1 3
9 9 7
8 8 6
4 5 4
样例输出
17.41
评测用例规模与约定
对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000,0 <= x, y, h <= 10000。
答案
这就是直接问最小生成树的问题了,连通就是相当于树,还要代价最小就是最小生成树
只不过这里的评测用例中n的数量级达到1000,那么就只能用Kruskal算法了,因为prim算法的时间复杂度是n^3,会运行超时的
package competition2;
import java.util.Scanner;
public class Main6
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int nodes=in.nextInt();
//表示每两个村庄直接的距离,这里对应通电的费用
double[][] weight=new double[nodes][nodes];
//表示每一个村庄的位置
int[][] location=new int[nodes][3];
//表示有多少条边
int EdgeNumber=0;
//下面初始化村庄位置和费用数组
for(int i=0;i<nodes;i++)
{
location[i][0]=in.nextInt();
location[i][1]=in.nextInt();
location[i][2]=in.nextInt();
}
for(int x=0;x<weight.length;x++)
{
for(int y=x+1;y<weight.length;y++)
{
weight[x][y]=Math.sqrt(
Math.pow((location[x][0]-location[y][0]),2)+
Math.pow((location[x][1]-location[y][1]),2))+
Math.pow((location[x][2]-location[y][2]),2);
EdgeNumber++;
}
}
//用来存储边的数组,并且初始化
Edge[] edges=new Edge[EdgeNumber];
int index=0;
for(int x=0;x<weight.length;x++)
{
for(int y=x+1;y<weight[x].length;y++)
{
edges[index++]=new Edge(x, y, weight[x][y]);
}
}
//然后对边进行排序
for(int x=0;x<edges.length-1;x++)
{
for(int y=0;y<edges.length-x-1;y++)
{
if(edges[y].weight>edges[y+1].weight)
{
Edge temp=edges[y];
edges[y]=edges[y+1];
edges[y+1]=temp;
}
}
}
double count=0;
//用来表示每一个节点的顶点是谁
int[] ends=new int[nodes];
//然后遍历每一条边,看看是否需要加入到最小生成树里面,
//然后我们的需求还挺简单的,就只需要最后一个费用,
//所以我们就只需要保存加入最小生成树的边的权重和就行
for(int x=0;x<EdgeNumber;x++)
{
//然后想得到这条边的两个节点的顶点是否是同一个顶点,不是同一个就加入
int end1=edges[x].start;
int end2=edges[x].end;
while(ends[end1]!=0)
{
end1=ends[end1];
}
while(ends[end2]!=0)
{
end2=ends[end2];
}
if(end1!=end2)
{
ends[end1]=end2;
count +=edges[x].weight;
}
}
System.out.printf("%.2f",count);
}
}
class Edge
{
int start;
int end;
double weight;
public Edge(int start, int end, double weight)
{
this.start = start;
this.end = end;
this.weight = weight;
}
}