去年(2014)编程之美我是靠着不熟练的C#,边做CodeHunt边查资料,跌跌撞撞进了复赛(当然进复赛以后没别人有技巧,没进线下,不然就醉了),当然没收藏好总结,有点遗憾——这个模式挺好玩的啊~于是今年就下定决心,做完以后整理一下所有题目的测试数据、我的代码和做题思路了~
先说明:我C#用的比Java熟,只愿意用C#写(虽然其实都不算太熟)
首先当然是资格赛的了
0.2
x和y做的运算?一般就是a*x+b*y+c的形式吧,套进去就行了
using System;
public class Program {
public static int Puzzle(int x, int y) {
return 2*x+y;
}
}
0.3
只能说,不难,这只是直觉,所写即所想……
using System;
public class Program {
public static bool Puzzle(int x, int y) {
return x>=y;
}
}
0.4
我下面的代码,只是为了强行用foreach而这么写的,其实完全没必要……for循环修改a[]里的,负数就绝对值运算一下就行了。
using System;
using System.Collections.Generic;
public class Program {
public static int[] Puzzle( int[] a ) {
List<int> t = new List<int>();
foreach (var i in a) {
t.Add(i < 0 ? -i : i);
}
return t.ToArray();
}
}
0.5
找一下规律,发现要求第一次出现的下标,否则返回-1——string的IndexOf方法正好就是这种行为!
using System;
public class Program {
public static int Puzzle(string s1, string s2) {
return s1.IndexOf(s2);
}
}
0.6
如果出现重复的数,返回true,否则返回false
一开始写了个,把数组排序,然后检查每个相邻两项是否相同——代码质量1灯
不得不说,C#的Linq太方便了!
对数组调用Distinct方法,返回一个IEnumerable<T>接口,然后ToArray()就变成去重的数组了,判断前后两者长度是否一样就行
using System;
using System.Linq;
public class Program {
public static bool Puzzle( int[] a ) {
return a.Distinct().ToArray().Length!=a.Length;
}
}
0.7
这个题是有点逗你玩的意思
我本来以为是判断奇数的个数,奇数个返回1,偶数个返回0-_-||
后来多搞了一点数据出来发现,好像直接输出最后一个值就好了!
using System;
public class Program {
public static int Puzzle(int[] a) {
return a[a.Length-1];
}
}
1.1
任务,比较明显的,就是把5位二进制字符串变成2位16进制字符串(不足的补上前导0)
我第一时间想到Java,因为的确用Java做过这件事情……可是,我可以吐槽一下,CodeHunt的Java简直是残次品吗?java.math.*没有是什么心态……
然后google查啊,然后就发现了我在代码里用到的下面这两个方法:
Convert.ToInt32
Int32.ToString()
他们可以做2、8、10、16进制之间的相互转换,没Java的凶悍(Java可以做2到36进制的),但这题上,够用了。
using System;
public class Program {
public static string Puzzle(string x) {
int ans=Convert.ToInt32(x,2);
return ans.ToString("X2");
}
}
1.2
通过骗数据大法(函数里把给出的输入和对应输出都正确处理,提交,他会给你更多数据),发现结果就是4的倍数……
0,1,2,3-->0
4,5,6,7-->4
——这明显是整除4后乘4的行为
using System;
public class Program {
public static int Puzzle(int n) {
return n/4*4;
}
}
1.3
(有一种数字电路课程设计的既视感)
看到数据的第一反应是,3个中2个以上为true的就是true(3选2投票器)
然后提交,返回错误,多提供一组:
true , false, false -->true
于是问题变成了,如果把x,y,z看做二进制每一位,拼起来表达的数>=3为true,否则为false
于是可以写成x||(y&&z)了
using System;
public class Program {
public static bool Puzzle(bool x, bool y, bool z) {
return x||y&&z;
}
}
1.4
灵感来源是," ! ! "-->{"",“!”,“!”,""}
这好像就是按空格分割的行为吧~
string的Split()大法好
using System;
public class Program {
public static string[] Puzzle(string s) {
return s.Split(' ');
}
}
1.5
简单的眼力和C#数组的考察
显然,生成一个m行n列的矩阵,每个元素[i][j]=(i+1)*(j+1)就行了
using System;
public class Program {
public static int[][] Puzzle(int m, int n) {
int [][]ans=new int[m][];
for(int i=0;i<m;i++){
ans[i]=new int[n];
for(int j=0;j<n;j++)
ans[i][j]=(i+1)*(j+1);
}
return ans;
}
}
1.6
观察最后一组数据,从第一个字母开始,间隔1个字母取出来组成新串,且不取最后一个字母
这个题粗暴的表达就行了
using System;
public class Program {
public static string Puzzle(string s) {
string ans="";
for(int i=0;i<s.Length-1;i+=2)
ans+=s[i];
return ans;
}
}
2.1
又是找x和y关系的?
再试试看放到a*x+b*y+c的关系式里看看,又好像有一组符合的解啊
using System;
public class Program {
public static int Puzzle(int x, int y) {
return x+2*y+3;
}
}
2.2
任务比较显然,在s串每两个字母中间插入一个p串
至于怎么写最短,就要看你是不是熟悉C#里String类的操作了
先用ToCharArray()方法变成字符数组,然后用Join()方法,轻松又愉快
——不熟悉的同学请拿起你的google,再不济,百度,搜一搜,看看msdn里的说明就行
using System;
public class Program {
public static string Puzzle(string s, string p) {
return String.Join(p,s.ToCharArray());
}
}
2.3
又靠猜了……
先观察下面小的一组{15,0},{1,11}-->{15,165}
165=11*15
然后放到第二组里看看
472-13*10=342=19*18
于是第二维就是p[0]*q[1]+p[1]*q[0]
第一维,真的就靠神奇的瞎猜了,反正p、q的4个数都用上看看,就找到规律了
using System;
public static class Program {
public static int[] Puzzle(int[] p, int[] q) {
return new int[2]{p[0]*q[0]-p[1]*q[1] , p[0]*q[1]+p[1]*q[0]};
}
}
2.4
这题略神,构造了很多组数据,还是没找到什么好规律……
直到突然想看看,a尽量减去b,看看为true的都余下多少……
为true的都余下2!
就是说,判断a%b==2就行了!
using System;
public class Program {
public static bool Puzzle(int a, int b) {
return a%b==2;
}
}
2.5
把字母变成相应的2位十进制数,拼接起来,返回一个int
为什么要一开始ans="0"?防止产生的ans串是空串,直接去Parse()会出Exception
using System;
public class Program {
public static int Puzzle(string s) {
string ans="0";
for(int i=0;i<s.Length;i++) ans+=((int)s[i]-96).ToString("D2");
return int.Parse(ans);
}
}
2.6
产生的结果好大啊……
我们除除看吧……
319935/462=692.5
692.5/462=1.49......... ==>这个方向仿佛不科学,换一个
692.5-462=230.5
而230.5=462/2-0.5
再来一组数据看看?
365807/494=740.5
740.5-494=246.5
而246.5=494/2-0.5
——恭喜,我们应该是找到规律了!
别忘了直接写表达式算出来double,强制类型转换就行了
using System;
public class Program {
public static int Puzzle(int x) {
return (int)(x*(x+x/2.0-0.5));
}
}
======================难度提升分割线======================
上面的代码都还是很短的吧?用户写的内容不超过10行,规律比较好找,也好写一点
下面的难度是比上面的大一些的,拿来当复赛最后一题估计不过分的~
(不过还是比hihocoder水啊哈哈哈~)
3.1
任务:给你一个可能带有其他字符的括号串,不合法串返回0,否则返回最大括号嵌套深度
一开始感觉很虚啊,因为直觉告诉我,写完也得来个十多行,估计满灯不了~可是当我写完提交后发现,代码怎么长随便……
下面的方法太丑了——手工维护一个栈,里面存放左括号的位置,碰到左括号压栈,碰到右括号试图出栈,并且在左括号到右括号之间找最大括号深度,这对括号的最大深度就+1,最后判断整个过程中是否出现非法行为,如果有就返回0,否则返回最大括号深度。
——发誓,去年做编程之美的CodeHunt,我真的没碰到过代码要这么长的……
using System;
using System.Linq;
public class Program {
public static int Puzzle(string s) {
int []dp=new int[s.Length];
int []stack=new int[s.Length];
int sp=0,mark=0;
for(int i=0;i<s.Length;i++){
if(s[i]=='('){
stack[sp++]=i;
}else if(s[i]==')'){
if(sp>0){
int m=0;
for(int j=stack[sp-1]+1;j<i;j++)
if(m<dp[j])m=dp[j];
dp[i]=m+1;
sp--;
}else mark=1;
}
}
return mark==1||sp!=0?0:dp.Max();
}
}
3.2
一开始看到这个题,我是拒绝的——这是什么鬼……,到底要做什么运算?于是跳过
可是回来一看,发现规律了:
b是你存的数据,a是需要的数据的下标,你返回a中请求的数据的查询结果值,组成个数组就行了……
不过有点小坑的是,a[]和b[]居然会传入同一个对象!你直接修改a再返回会得到错误答案(不过本来就不应该这样做的……)
using System;
public static class Program {
public static int[] Puzzle(int[] a, int[] b) {
int []ans=new int[a.Length];
for(int i=0;i<a.Length;i++)
ans[i]=b[a[i]];
return ans;
}
}
3.3
这个任务挺诡异的——
要求找到在s中出现的所有p串,删除,其中最后一次出现p串的位置之后的字符也要删除
比如
"acba","acb"==>""
"bababjccccccccaba","ab"==>"bjcccccccc"
"bbb","bb"==>""
一种想法就是,先找LastIndexOf(),删掉之后的字符,然后再在剩下的s串中找p串,用Replace全部替换掉
——然后"bbb","bb"你想怎么办啊?
于是我采用了一种比较不科学的折衷方法:
先把所有p串换成###,然后删除最后一次###后面的字符,最后把所有###换成空,然后,好像就通过了?
(希望有大神给个更科学的正解,不胜感激~)
using System;
public class Program {
public static string Puzzle(string s, string p) {
if(p.Length==0)return s;
s=s.Replace(p,"###");
if(s.LastIndexOf("###")>=0)s=s.Remove(s.LastIndexOf("###"));
s=s.Replace("###","");
return s;
}
}
3.4
任务,找到x中出现的最大的质因子……
这个题就八仙过海,各显神通了,我这个写法应该不是最优的,请无视……
using System;
public class Program {
public static int Puzzle(int x) {
if(x<4)return x;
int maxp=1;
for(int i=2;i<=Math.Sqrt(x);i++)
while(x%i==0){
maxp=i;
x/=i;
}
return maxp>x?maxp:x;
}
}
3.5
感觉好像做了什么很飘逸的运算产生的结果……
提示也很有意思,How is the array transformed?数组做了变换?
于是猜猜看,感觉是每项取了个平均值?
{0,18,30,40}-->{9,16,29,35}
9==(0+18)/2
16!=(0+30)/2;但16==(0+18+30)/3!!!
29=(18+30+40)/3
35=(30+40)/2
……怎么感觉像是图像处理里的均值滤波器啊(不过是一维的)
然后写一发啊,提交,过了
using System;
public class Program {
public static int[] Puzzle(int[] a) {
int []b=new int[a.Length];
b[0]=(a[0]+a[1])/2;
for(int i=1;i<a.Length-1;i++)
b[i]=(a[i-1]+a[i]+a[i+1])/3;
b[a.Length-1]=(a[a.Length-2]+a[a.Length-1])/2;
return b;
}
}
3.6
我发誓,没左上角的提示,还真的想不出来!
What happens to the bit pattern?
bit?感觉是二进制啊,于是:
Sample的二进制表示:00000001
10000000
10000100
00100001
01100101
10100110
规律:二进制串做了个reverse操作
于是又来,把int变成8位2进制字符串,然后反转,再Convert.ToInt32转成整数形式。
不过,不知道为什么,CodeHunt上以下代码运行报错,不能用下面的办法反转原串(下面的办法网上找的,看着好科学)
char[] ret = data.ToCharArray();
return string.Concat<char>(ret.Reverse<char>());
那就折衷一下吧,变成字符数组,然后反转数组,你总没意见了吧?
using System;
public static class Program {
public static int Puzzle(int n) {
char []arr=Convert.ToString(n,2).PadLeft(8, '0').ToCharArray();
Array.Reverse(arr);
return Convert.ToInt32(new string(arr),2);
}
}
===================================================
总结:
打完资格赛的CodeHunt,感觉是方方面面都要考啊!
C#要重点关注:
1、字符串的操作(字符串与数的转换,字符串的查找、替换、分割等)
2、数组的属性、方法
3、Linq带来的一些超省力的方法(比如Sum\Distinct\Min\Max\Zip等,有些地方不用Linq代码质量分拿不到3灯的)
然后还有就是自己平时的算法能力和实现能力的积累了
就是这样,End at 2015.04.20