加法变乘法
我们都知道:1+2+3+ ... + 49 = 1225
现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015
比如:
1+2+3+...+10*11+12+...+27*28+29+...+49 = 2015
就是符合要求的答案。
请你寻找另外一个可能的答案,并把位置靠前的那个乘号左边的数字提交(对于示例,就是提交10)。
注意:需要你提交的是一个整数,不要填写任何多余的内容。
16
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner in=new Scanner(System.in);
for(int i=1;i<=46;i++){
for(int j=i+2;j<=48;j++){
p(i,j);
}
}
}
public static void p(int i,int j){
if((1225-2*i-2*j-2+i*(i+1)+j*(j+1))==2015)
System.out.println(i+" "+j);
}
}
分析:这道题很简单,无非从1开始一直到最后一位数遍历,看看乘号放在哪里能使等式成立。
总结:对于穷举型算法题,需要找到题目的各种可能的情况,然后编写代码
牌型种数
小明被劫持到X赌城,被迫与其他3人玩牌。
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,自己手里能拿到的初始牌型组合一共有多少种呢?
请填写该整数,不要填写任何多余的内容或说明文字。
3598180
import java.util.ArrayList;
public class Main {
static int answ=0;
public static void main(String[] args)throws Exception{
ArrayList al=new ArrayList();
for(int i=1;i<=13;i++) {
al.add(i);
p(al);
al.remove(0);
}
System.out.println(answ);
}
public static void p(ArrayList al) {
if(al.size()==13) {
//如果牌数达到13张,总数加一,当前递归结束
answ++;
return;
}
if(al.size()<4||al.get(al.size()-4)!=al.get(al.size()-1)) {
//如果最后四张不是相同牌,就可以继续加这张牌入卡组
al.add(al.get(al.size()-1));
p(al);
al.remove(al.size()-1);
}
for(int i=al.get(al.size()-1)+1;i<=13;i++) {
//加比最后一张大i的牌入卡组
al.add(i);
p(al);
al.remove(al.size()-1);
}
}
}
本来不准备写这道题的解析的,但是因为这道题是填空题,所以搜索网上的答案发现基本上都是无技巧穷举,于是想写出一个时间复杂度很低的算法解此题。
分析:13张穷举自然可行,但是如果想降低时间复杂度,需要考虑如何使每一次递归得出的最终结果都是可行的(符合牌型条件)而不做淘汰处理,则可使时间复杂度最低。
那么就在普通递归的前提下,加上一个从小到大依次加入卡牌的条件,然后如果最后四张相同,就不能继续加此卡牌,此时时间复杂度则最低。
总结:越是条件少的题目,越需要自己根据编写的代码创造新的判断条件,这样才能使时间复杂度进一步降低。
饮料换购
乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去,但不允许赊账。
请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入的n瓶饮料,最后他一共能得到多少瓶饮料。
输入:一个整数n,表示开始购买的饮料数量(0
例如:
用户输入:
100
程序应该输出:
149
用户输入:
101
程序应该输出:
151
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
import java.util.Scanner;
public class Main {
static int sum,n;
public static void main(String[] args){
Scanner in=new Scanner(System.in);
n=in.nextInt();
sum=n;
p(n);
System.out.println(sum);
}
public static void p(int num){//上一次喝完饮料剩下的空瓶子
if(num<3)//如果只剩不到3个瓶子结束递归
return;
sum+=num/3;
p(num/3+num%3);//返回的值为喝的饮料数和不能兑换饮料的空瓶子数
}
}
分析:这道题是小学的竞赛题改编,无非最简单的递归,并且递归的参数只有一个,那就是上一次喝饮料剩下的空瓶子数量,当空瓶子小于3即可结束递归,输出。
总结:递归的参数传递个数和方式很重要,务必清楚参数的定义,否则递归很容易出现死循环和错误输出
垒骰子
赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。
经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。 atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
由于方案数可能过多,请输出模 10^9 + 7 的结果。
不要小看了 atm 的骰子数量哦~
「输入格式」
第一行两个整数 n m
n表示骰子数目
接下来 m 行,每行两个整数 a b ,表示 a 和 b 不能紧贴在一起。
「输出格式」
一行一个数,表示答案模 10^9 + 7 的结果。
「样例输入」
2 1
1 2
「样例输出」
544
「数据范围」
对于 30% 的数据:n <= 5
对于 60% 的数据:n <= 100
对于 100% 的数据:0 < n <= 10^9, m <= 36
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
static long sum=0;
static int n,m;
static int[][] ar;
static ArrayList[] al;
public static void main(String[] args){
Scanner in=new Scanner(System.in);
n=in.nextInt();
m=in.nextInt();
al=new ArrayList[7];//建立一个集合数组,这个集合数组里每个集合存放的是第一个骰子上方的点数对应上一层骰子上方可行的点数
for(int i=1;i<7;i++){
al[i]=new ArrayList();//集合数组的初始化
for(int p=0;p<=6;p++){
al[i].add(p);//每个集合类里放1到6六个数
}
}
for(int i=0;i不可行就把点数变成0
}
for(int i=1;i<7;i++){
for(int j=1;j ala=new ArrayList();
for(int i=0;i alb=new ArrayList();
for(int i=0;i
分析:考点:1.快速幂
2.递归
3.动态规划
4.矩阵
不过我还是认为,所谓的动态规划,深度优先遍历什么的不过是一个名字,最重要的是理解程序的思想,而非动态规划什么的单独的算法。
所以这道题的考点:1.数学中的快速幂,2.有记忆的递归,3
从题目中考虑,输入的n为10亿,而程序的运行超过100亿就有可能会超时(超过一秒),所以自然不能用普通的递归一层一层的计算,但是想拿到这道题的满分,就必须从小数据入手,弄清程序运行的思路,再想办法优化程序的时间复杂度。
正常的优解思路:
每一层都通过上一次可能的点数的每个点数次数并且依靠基层计算得出,然后再进行下一次的计算,知道循环n次。
快速幂的优解思路:
我们如果算出第8层的每个点数的次数,那么完全可以通过第八层得出第16层的点数次数(稍微理解一下快速幂就可以得出)。
思路主要还是依靠理解代码,文字的解说往往太过无力。
而且为了进一步的优化时间复杂度,可以进一步优化,如:我求出了16层的点数次数,下一次计算128层的,我就可以之前记录一下16层的点数次数,然后继续类矩阵计算。
总结:
1.分析问题需要找到问题的本质所在,如本题:六面骰子,任何一层侧面都是四种可能,所以我可以把骰子想象成两面(上下两面的),然后4面的计算,在最终的时候再乘以一个4^n即可,——6变2;然后我又考虑可以直接从n层上面对立n+1层下面变成n层上面对立n+1层上面,这就直接变成骰子只有一个面,然后根据一个面进行动态规划和快速幂即可得出答案。
2.对于一个程序,最重要的不是每个地方都进行时间复杂度的优化,而是尽可能的对大量循环的语句进行优化即可,所以我们可以把程序看做两部分,一部分是核心的循环判断地方,另一部分是尽可能的准备工作,我们的准备工作的代码是完全为循环代码服务的,最高级别目的就是尽可能的减少循环代码的时间复杂度,也就是语句的数量。
3.算法是极富美感和绚丽的,任何一个复杂的算法,不在于他使用了什么方法,而是他使用什么方法最适用于那个问题,运用不同的算法思路求解同一问题,得到的时间复杂度是大大不同的,并不是越难的算法就越适合使用,算法技巧必须是跟随题目时势的而不是看到题目就用最厉害的算法。
4.任何一个具有规律性的计算,都有其更方便的算法的可能,学习越多的算法只能说可以在自己做题的时候拥有更多的武器去破解这个问题,但是并不能说武器越多,就越有可能攻破,武器越多只能说见识越光,处理问题的选择越多,而“万法自然”,算法再厉害也是一种思想,只有思维越厉害,才能说能力越强。
5.在编写程序的时候,时刻考虑自己写的代码有需要注意什么,比如如果一个变量是一个基数,那么要把它final,避免后面自己无意修改,而做软件用的private也是如此,时刻注意自己即将要走的下一步可能导致什么——溢出?变量值的丢失?语句的错误使用?,代码往往不会是网上那些电影一样帅气优雅,而是步步惊心,如履薄冰。
生命之树
在X森林里,上帝创建了生命之树。
他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。
上帝要在这棵树内选出一个非空节点集S,使得对于S中的任意两个点a,b,都存在一个点列 {a, v1, v2, ..., vk, b} 使得这个点列中的每个点都是S里面的元素,且序列中相邻两个点间有一条边相连。
在这个前提下,上帝要使得S中的点所对应的整数的和尽量大。
这个最大的和就是上帝给生命之树的评分。
经过atm的努力,他已经知道了上帝给每棵树上每个节点上的整数。但是由于 atm 不擅长计算,他不知道怎样有效的求评分。他需要你为他写一个程序来计算一棵树的分数。
「输入格式」
第一行一个整数 n 表示这棵树有 n 个节点。
第二行 n 个整数,依次表示每个节点的评分。
接下来 n-1 行,每行 2 个整数 u, v,表示存在一条 u 到 v 的边。由于这是一棵树,所以是不存在环的。
「输出格式」
输出一行一个数,表示上帝给这棵树的分数。
「样例输入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
「样例输出」
8
「数据范围」
对于 30% 的数据,n <= 10
对于 100% 的数据,0 < n <= 10^5, 每个节点的评分的绝对值不超过 10^6 。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
static int n;
static long[] arvalue;
static int[] av;
static ArrayList[] alpoint;
static long sum=-1000000;
static boolean[] bo;
public static void main(String[] args){
Scanner in=new Scanner(System.in);
n=in.nextInt();
bo=new boolean[n+1];
arvalue=new long[n+1];
av=new int[n+1];
for(int i=1;i<=n;i++){
int s=in.nextInt();
arvalue[i]=s;
av[i]=s;
}
alpoint=new ArrayList[n+1];
for(int i=1;i<=n;i++){
alpoint[i]=new ArrayList();
}
for(int i=1;i0?Math.max(max,p((int)alpoint[pi].get(i),pi)):0;
}
arvalue[pi]+=max;
bo[pi]=true;
}
sum=Math.max(sum,arvalue[pi]);
return arvalue[pi];
}
}
分析:这道题难点在于把题目分析清楚,整理成适合编程的文段:在一个树中,有若干个结点集合,每个集合规定:每两个集合中的节点之间相连的都在集合中。
于是就变成一道很简单的树状dp问题,只需要从根节点依次往下有记忆的递归即可。