昨晚听歌的时候总算把在UPENN毕业典礼上听到的那首古典乐的名字搞到手了,之前怎么都没找到,果然还是可遇而不可求。在此献上《威风凛凛进行曲第一号》:
Pomp and Circumstance Marches NO.1
昨晚失眠到5am,于是早上睡到了12am,颓了一个大好时光。
下午去到图书馆,我花了大概40分钟去阅读写test case的课件和看了一下相关视频,大多数都是讲软件开发的测试流程,但其实我只是想弄明白优秀的程序员写完算法之后应该如何测试程序的正确性。在stackOverflow发现了一个答案,被雷到了,但是又觉得很有道理:stackoverflow
我觉得第二个回答很有意思,大概是说牛逼程序猿在参加ACM比赛的时候对于general case的处理一般都正确,所以只要处理corner case和overflow两种情况就可以。
不过我还是尝试给pizza那道题目写了30个test cases,有以下收获:
#1 程序有一些bug被检查出来了
#2 test case在写的时候,竟然也有一个答案是错的。这的确设计到validation的问题——就是一定还要有人来验证test case的正确性
#3 目前我还是用black box的思维去写test case,把输入分成某几种类别,自己在写test case的时候最好写注释(在解析的时候忽略注释就可以了)
==================================
所以我下午写了一个简单的Checker类,写了30个简单的test cases,以及修复的(1)中程序的bug(超级多啊汗 |||= . =)
Checker类:
/* Checker class to check the result from file 1 and file 2 */
class Checker {
public static void check(String fn1, String fn2) {
File f1 = new File(fn1);
File f2 = new File(fn2);
try {
BufferedReader br1 = new BufferedReader(new FileReader(f1));
BufferedReader br2 = new BufferedReader(new FileReader(f2));
String s1, s2;
boolean correct = true;
while ((s1 = br1.readLine()) != null) {
if ((s2 = br2.readLine()) == null) {
System.out.println("lines don't match in file1 and file2!");
correct = false;
break;
}
if (!s1.equals(s2)) {
System.out.println("file1 is " + s1);
System.out.println("file2 is " + s2);
correct = false; // do not need to break, continue to check other bugs
}
}
if ((s2 = br2.readLine()) != null) {
System.out.println("lines don't match in file1 and file2!");
correct = false;
}
if (correct) {
System.out.println("Correct!");
} else {
System.out.println("fix bug!");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
test case:
输入:
30
3 1 1 1
3 1 2 1
3 2 1 1
3 1 1 2
3 1 2 3
3 2 3 1
3 3 2 1
6 1 1 1 1 1 1 // 相等
6 1 2 1 1 2 1
6 8 9 8 1 1 1 // greedy not work
6 1 3 1 2 5 2 // 隔开两部分
6 2 2 4 6 5 2 // 中间取一部分
6 2 1 1 2 1 1 // rotate 上面的输入
6 8 1 1 1 8 9
6 1 2 5 2 1 3
6 2 4 6 5 2 2
9 1 1 1 1 1 1 1 1 1
9 1 3 1 2 4 2 5 5 5
9 2 5 2 1 3 1 2 4 1 // 三部分
9 2 8 3 2 2 4 6 5 2 // 取第一部分,后面6个要从中间取
9 5 5 1 2 6 3 5 4 4 // 类似括号一样的嵌套
9 3 1 2 4 2 5 5 5 1 // rotate上面的输入
9 1 2 4 2 5 5 5 1 3
9 5 2 1 3 1 2 4 1 2
9 2 1 3 1 2 4 1 2 5
9 8 3 2 2 4 6 5 2 2
9 3 2 2 4 6 5 2 2 8
9 5 1 2 6 3 5 4 4 5
9 1 2 6 3 5 4 4 5 5
12 5 1 2 6 3 5 4 4 5 3 2 1
输出:
Case #1: 1
Case #2: 2
Case #3: 2
Case #4: 2
Case #5: 3
Case #6: 3
Case #7: 3
Case #8: 2
Case #9: 4
Case #10: 16
Case #11: 8
Case #12: 9
Case #13: 4
Case #14: 16
Case #15: 8
Case #16: 9
Case #17: 3
Case #18: 14
Case #19: 12
Case #20: 17
Case #21: 16
Case #22: 14
Case #23: 14
Case #24: 12
Case #25: 12
Case #26: 17
Case #27: 17
Case #28: 16
Case #29: 16
Case #30: 21
修改之后的代码:
import java.util.*;
import java.io.*;
public class Solution {
public static void main(String[] args) {
File inFile = new File ("test-case.in");
File outFile = new File ("test-case.out");
try {
BufferedReader br = new BufferedReader(new FileReader(inFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(outFile));
int T = Integer.parseInt(br.readLine());
for (int i = 1; i <= T; i++) {
String s = br.readLine();
String[] parts = s.split("\\s");
int N = Integer.parseInt(parts[0]);
int[] pizza = new int[N];
for (int j = 0; j < N; j++) {
pizza[j] = Integer.parseInt(parts[j+1]);
}
bw.write("Case #" + i + ": " + maxPizzaValue(pizza) + "\n");
}
bw.close();
Checker.check("correct.out", "test-case.out");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// solve the problem
public static int maxPizzaValue(int[] pizza) {
int n = pizza.length;
int maxPizza = 0;
// copy pizza to pizza
int[] bigPizza = new int[2*n];
System.arraycopy(pizza, 0, bigPizza, 0, n);
System.arraycopy(pizza, 0, bigPizza, n, n);
int[][] dp = calDP(n, bigPizza);
for (int x = 0; x < n-2; x++) {
for (int y = x+1; y < n-1; y++) {
for (int z = y+1; z < n; z++) {
// check the valid division
if ((y - x - 1) % 3 == 0 && (z - y - 1) % 3 == 0) {
int val =
max(pizza[x], pizza[y], pizza[z]) + dp[x+1][y-1] + dp[y+1][z-1] + dp[z+1][x-1+n];
if (val > maxPizza) maxPizza = val;
}
}
}
}
return maxPizza;
}
// calculate dp elements
// dp[i][j] means max value from index i to j
public static int[][] calDP (int n, int[] A) {
int[][] dp = new int[2*n][2*n];
// init
for (int i = 0; i < 2*n; i++) {
for (int j = 0; j < 2*n; j++) {
dp[i][j] = 0;
}
}
// base case
for (int i = 0; i <= 2*n-3; i++) {
dp[i][i+2] = A[i+1]; // this is a line, not a circle, must take the middle one
}
int len = 6;
while (len <= n) {
for (int i = 0; i <= 2*n-len; i++) {
int j = i+len-1;
// must take the i, j at the same round
for (int k = i+1; k < j; k += 3) {
int val = A[k];
val += dp[i+1][k-1];
val += dp[k+1][j-1];
dp[i][j] = dp[i][j] > val ? dp[i][j] : val;
}
// take the i, j at different round
for (int k = i; k < j; k++) {
if ((k - i + 1) % 3 == 0) {
int val = dp[i][k] + dp[k+1][j];
dp[i][j] = dp[i][j] > val ? dp[i][j] : val;
}
}
}
len += 3;
}
return dp;
}
// return the max of the 3
public static int max(int x, int y, int z) {
int max = x > y ? x : y;
max = max > z ? max : z;
return max;
}
}