计蒜之道2015程序设计大赛初赛第二场——人人都有极客精神

(一)体面

人人公司是一家极为鼓励极客精神的公司,当有重要的项目需要上线但又时间太紧,甚至需要当天上线的时候,往往会挂起海盗旗开启电子日期显示,让大家可以在对时间有更明确的感知的情况下,同心协力搞定重要的项目。海盗旗下方的电子屏显示的日期形式为 YYYYMMDD (年份占 4 位、月份占 2 位、天数占 2 位)。

日期电子屏幕上每个数字对应的显示如下图:

从上图可以得知每个数字对应的笔画数,比如 2 的笔画数是 5,8 的笔画数是 7,等等。人人员工小明看到了项目的启动日期 d,但是项目的结束日期没看清楚,只知道电子屏幕上项目结束日期所需的笔画数为 m,你能帮小明算出来项目执行所用的时间天数么?

输入格式

输入数据有多组。第一行输入一个整数 T (1 ≤ T ≤ 20),表示一共有 T 组数据。

接下来每组数据 2 行,共 T * 2 行。每组第一行输入一个长度为 8 的仅包含数字的字符串 d,表示项目的启动日期,形式为 YYYYMMDD。每组第二行输入一个非负整数 m (0 ≤ m ≤ 100),表示电子屏幕上项目结束日期所需的笔画数。输入日期保证合法。

输出格式

一共输出 T 行,每行一个整数,表示该组数据对应的项目执行所用的时间天数。如果最近的符合要求的结束日期超过 2999 年 12 月 31 日或无解则输出 -1,否则输出符合要求的最小的解。

样例1

输入:

2
20150718
30
29991231
38

输出:

85
-1
(二)参考源码
java实现是我自己写的,另推荐一个C++实现,代码非常简洁。http://www.cnblogs.com/Tinamei/p/4658133.html
  1 import java.io.BufferedReader;
  2 import java.io.InputStreamReader;
  3 import java.text.DateFormat;
  4 import java.text.ParseException;
  5 import java.text.SimpleDateFormat;
  6 import java.util.ArrayList;
  7 import java.util.Arrays;
  8 import java.util.Calendar;
  9 import java.util.HashMap;
 10 import java.util.LinkedHashMap;
 11 import java.util.Map;
 12 
 13 public class Main {
 14     private static BufferedReader reader = new BufferedReader(
 15             new InputStreamReader(System.in));
 16     // 每个数字的笔画数
 17     private static Map<String, Integer> numberMap = new HashMap<String, Integer>();
 18     // 每个月份的笔画数,例如2月是‘0’和‘2’的笔画数之和
 19     private static Map<String, Integer> monthMap = new HashMap<String, Integer>();
 20     // 每天的笔画数,例如31是‘3’和‘1’的笔画数之和
 21     private static Map<String, Integer> dayMap = new HashMap<String, Integer>();
 22     // 每天有多少个笔画,例如11月12日,1112=11
 23     private static Map<String, Integer> dateMap = new LinkedHashMap<String, Integer>();
 24     private static String monthArray[] = { "01", "02", "03", "04", "05", "06",
 25             "07", "08", "09", "10", "11", "12" };
 26     // 平年每个月有多少天,例如1月:01=31
 27     private static Map<String, Integer> daysEachMonthMap = new LinkedHashMap<String, Integer>();
 28     // key:笔画数 value:日期列表,日期自小到大.例如:11=[1112, 1113, 1115, 1121, 1211]
 29     private static Map<Integer, ArrayList<String>> countDateListMap = new HashMap<Integer, ArrayList<String>>();
 30     // key:笔画数 value:日期数组,日期自小到大.例如:11=[1112, 1113, 1115, 1121, 1211]
 31     private static Map<Integer, String[]> countDateArrayMap = new HashMap<Integer, String[]>();
 32     // 2月29有多少画
 33     private static final int COUNT_0229 = 6 + 5 + 5 + 6;
 34     // 2月29的字符串表示
 35     private static final String STRING_0229 = "0229";
 36     // 换行符
 37     private static final String LINE_SEPARATOR = System
 38             .getProperty("line.separator");
 39     private static final DateFormat dateFormat = new SimpleDateFormat(
 40             "yyyyMMdd");
 41     // 每个月有多少天
 42     private static int[] daysEachMonthArray = { 0, 31, 28, 31, 30, 31, 30, 31,
 43             31, 30, 31, 30, 31 };
 44     static {
 45         daysEachMonthMap.put("01", 31);
 46         daysEachMonthMap.put("02", 28);
 47         daysEachMonthMap.put("03", 31);
 48         daysEachMonthMap.put("04", 30);
 49         daysEachMonthMap.put("05", 31);
 50         daysEachMonthMap.put("06", 30);
 51 
 52         daysEachMonthMap.put("07", 31);
 53         daysEachMonthMap.put("08", 31);
 54         daysEachMonthMap.put("09", 30);
 55         daysEachMonthMap.put("10", 31);
 56         daysEachMonthMap.put("11", 30);
 57         daysEachMonthMap.put("12", 31);
 58 
 59         numberMap.put("0", 6);
 60         numberMap.put("1", 2);
 61         numberMap.put("2", 5);
 62         numberMap.put("3", 5);
 63         numberMap.put("4", 4);
 64         numberMap.put("5", 5);
 65         numberMap.put("6", 6);
 66         numberMap.put("7", 3);
 67         numberMap.put("8", 7);
 68         numberMap.put("9", 6);
 69 
 70         for (int i = 0; i < monthArray.length; i++) {
 71             monthMap.put(
 72                     monthArray[i],
 73                     numberMap.get(String.valueOf(monthArray[i].charAt(0)))
 74                             + numberMap.get(String.valueOf(monthArray[i]
 75                                     .charAt(1))));
 76         }
 77         for (int i = 1; i <= 31; i++) {
 78             String dayKey = null;
 79             if (i < 10) {
 80                 dayKey = "0" + i;
 81             } else {
 82                 dayKey = String.valueOf(i);
 83             }
 84             dayMap.put(dayKey, numberMap.get(String.valueOf(dayKey.charAt(0)))
 85                     + numberMap.get(String.valueOf(dayKey.charAt(1))));
 86         }
 87         for (Map.Entry<String, Integer> entry : daysEachMonthMap.entrySet()) {
 88             for (int i = 0; i < entry.getValue(); i++) {
 89                 String dayKey = null;
 90                 if (i < 9) {
 91                     dayKey = "0" + (i + 1);
 92                 } else {
 93                     dayKey = String.valueOf(i + 1);
 94                 }
 95                 dateMap.put(entry.getKey() + dayKey,
 96                         monthMap.get(entry.getKey()) + dayMap.get(dayKey));
 97             }
 98         }
 99         for (Map.Entry<String, Integer> entry : dateMap.entrySet()) {
100             if (!countDateListMap.containsKey(entry.getValue())) {
101                 countDateListMap.put(entry.getValue(), new ArrayList<String>());
102             }
103             countDateListMap.get(entry.getValue()).add(entry.getKey());
104         }
105         for (Map.Entry<Integer, ArrayList<String>> entry : countDateListMap
106                 .entrySet()) {
107             countDateArrayMap.put(entry.getKey(),
108                     entry.getValue().toArray(new String[0]));
109         }
110     }
111 
112     private static int readInt() throws Exception {
113         return Integer.parseInt(reader.readLine());
114     }
115 
116     /**
117      * 
118      * @param count
119      *            笔画数
120      * @param beginDate
121      * @return 大于等于beginDate,笔画数为count的最小日期;如果找不到,返回null
122      */
123     private static String getDate(int count, String beginDate) {
124         String[] dateArray = countDateArrayMap.get(count);
125         if (null == dateArray) {
126             return null;
127         }
128         if ("0101".equals(beginDate)) {
129             return dateArray[0];
130         } else {
131             // 二分法查找
132             int index = Arrays.binarySearch(dateArray, beginDate);
133             if (index >= 0) {
134                 return beginDate;
135             } else if (-(index + 1) == dateArray.length) {
136                 return null;
137             } else {
138                 return dateArray[-(index + 1)];
139             }
140         }
141     }
142 
143     /**
144      * 
145      * @return year的笔画数
146      */
147     private static int getYearCount(String year) {
148         return numberMap.get(String.valueOf(year.charAt(0)))
149                 + numberMap.get(String.valueOf(year.charAt(1)))
150                 + numberMap.get(String.valueOf(year.charAt(2)))
151                 + numberMap.get(String.valueOf(year.charAt(3)));
152     }
153 
154     /**
155      * 判断某年是否有2月29这天
156      */
157     private static boolean has0229(int year) {
158         if (year % 100 == 0) {
159             return year % 400 == 0;
160         } else {
161             return year % 4 == 0;
162         }
163     }
164 
165     /**
166      * 计算endDate和beginDate之间的天数,仅支持同一年的日期比较
167      */
168     @Deprecated
169     private static int getDays(String beginDate, String endDate) {
170         try {
171             Calendar begin = Calendar.getInstance();
172             begin.setTime(dateFormat.parse(beginDate));
173             Calendar end = Calendar.getInstance();
174             end.setTime(dateFormat.parse(endDate));
175             return end.get(Calendar.DAY_OF_YEAR)
176                     - begin.get(Calendar.DAY_OF_YEAR);
177         } catch (ParseException e) {
178             throw new RuntimeException(e);
179         }
180     }
181 
182     /**
183      * 考虑从1582年才开始现代闰年计算方式,对于本题,该方法仅适用于1582年以后的日期
184      */
185     @Deprecated
186     private static int getDaysBetween(String beginDate, String endDate) {
187         try {
188             long begin = dateFormat.parse(beginDate).getTime();
189             long end = dateFormat.parse(endDate).getTime();
190             return (int) ((end - begin) / (24 * 60 * 60 * 1000));
191         } catch (ParseException e) {
192             throw new RuntimeException(e);
193         }
194     }
195 
196     private static int getDaysBetween2(String beginDate, String endDate) {
197         int result = 0;
198         int beginDay = Integer.parseInt(beginDate.substring(6));
199         int beginMounth = Integer.parseInt(beginDate.substring(4, 6));
200         int beginyear = Integer.parseInt(beginDate.substring(0, 4));
201 
202         int endDay = Integer.parseInt(endDate.substring(6));
203         int endMounth = Integer.parseInt(endDate.substring(4, 6));
204         int endyear = Integer.parseInt(endDate.substring(0, 4));
205 
206         while (true) {
207             if (endyear == beginyear && endMounth == beginMounth
208                     && endDay == beginDay) {
209                 break;
210             }
211             if (has0229(beginyear)) {
212                 daysEachMonthArray[2] = 29;
213             } else {
214                 daysEachMonthArray[2] = 28;
215             }
216             beginDay++;
217             if (beginDay > daysEachMonthArray[beginMounth]) {
218                 beginDay = 1;
219                 beginMounth++;
220                 if (beginMounth > 12) {
221                     beginMounth = 1;
222                     beginyear++;
223                 }
224             }
225             result++;
226         }
227         return result;
228     }
229 
230     /**
231      * 计算给定日期的笔画数,仅用于测试
232      */
233     private static int getCount(String date) {
234         int result = 0;
235         for (int i = 0; i < date.length(); i++) {
236             result += numberMap.get(String.valueOf(date.charAt(i)));
237         }
238         return result;
239     }
240 
241     /**
242      * 对于不满4位的年份,前面用0补齐
243      */
244     private static String getYear(int year) {
245         StringBuilder builder = new StringBuilder();
246         builder.append(year);
247         while (builder.length() < 4) {
248             builder.insert(0, "0");
249         }
250         return builder.toString();
251     }
252 
253     public static void main(String[] args) throws Exception {
254 
255         int testCount = Main.readInt();
256         // 存放输出结果
257         StringBuilder builder = new StringBuilder();
258         for (int i = 0; i < testCount; i++) {
259             // 对于本组测试数据,是否已将结果存放到builder
260             boolean found = false;
261             String yearDate = reader.readLine();
262             int count = readInt();
263             String year = yearDate.substring(0, 4);
264             int yearInt = Integer.parseInt(year);
265             // 若果输入年份就大于或等于3000,直接输出-1
266             if (yearInt >= 3000) {
267                 builder.append(-1).append(LINE_SEPARATOR);
268                 found = true;
269                 continue;
270             }
271             // count 最大为56,此时日期是“08880808”
272             // count 最小是16,此时日期是“11111111”
273             if (count > 56 || count < 16) {
274                 builder.append(-1).append(LINE_SEPARATOR);
275                 found = true;
276                 continue;
277             }
278             String date = yearDate.substring(4);
279             int dateCount = count - getYearCount(year);
280             String expectedDate = getDate(dateCount, date);
281             if (null != expectedDate) {
282                 if (dateCount == COUNT_0229
283                         && expectedDate.compareTo(STRING_0229) > 0
284                         && STRING_0229.compareTo(date) >= 0 && has0229(yearInt)) {
285                     builder.append(
286                             getDaysBetween2(yearDate, year + STRING_0229))
287                             .append(LINE_SEPARATOR);
288                     found = true;
289                     continue;
290                 } else {
291                     builder.append(
292                             getDaysBetween2(yearDate, year + expectedDate))
293                             .append(LINE_SEPARATOR);
294                     found = true;
295                     continue;
296                 }
297             } else {
298                 if (dateCount == COUNT_0229 && STRING_0229.compareTo(date) >= 0
299                         && has0229(yearInt)) {
300                     builder.append(
301                             getDaysBetween2(yearDate, year + STRING_0229))
302                             .append(LINE_SEPARATOR);
303                     found = true;
304                     continue;
305                 } else {
306                     // no thing to do
307                 }
308             }
309 
310             for (int j = yearInt + 1; j < 3000; j++) {
311                 dateCount = count - getYearCount(getYear(j));
312                 // count 最大为26,此时日期是“0808”
313                 // count 最小是8,此时日期是“1111”
314                 if (dateCount > 26 || dateCount < 8) {
315                     continue;
316                 }
317                 expectedDate = getDate(dateCount, "0101");
318                 if (null != expectedDate) {
319                     if (dateCount == COUNT_0229
320                             && expectedDate.compareTo(STRING_0229) > 0
321                             && has0229(j)) {
322                         builder.append(
323                                 getDaysBetween2(yearDate, getYear(j)
324                                         + STRING_0229)).append(LINE_SEPARATOR);
325                         found = true;
326                         break;
327                     } else {
328                         builder.append(
329                                 getDaysBetween2(yearDate, getYear(j)
330                                         + expectedDate)).append(LINE_SEPARATOR);
331                         found = true;
332                         break;
333                     }
334                 } else {
335                     if (dateCount == COUNT_0229 && has0229(j)) {
336                         builder.append(
337                                 getDaysBetween2(yearDate, getYear(j)
338                                         + STRING_0229)).append(LINE_SEPARATOR);
339                         found = true;
340                         break;
341                     } else {
342                         continue;
343                     }
344                 }
345             }
346             if (!found) {
347                 builder.append(-1).append(LINE_SEPARATOR);
348             }
349         }
350         System.out.println(builder.toString());
351     }
352 }
Java

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 typedef long long LL;
 7 int year[3000], month2[13], day[40];
 8 int s[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};   // 存第几个数需要几笔画数
 9 int month1[2][13] = {0,31,28,31,30,31,30,31,31,30,31,30,31,0,31,29,31,30,31,30,31,31,30,31,30,31};   // month[0]存不是闰年当前月份天数
10 void init ()
11 {
12     for (int i=0; i<40; i++)
13         day[i] = s[i%10] + s[i/10];  // 第几天所需笔画数,第一天是0和1所需笔画数之和
14     for (int i=0; i<13; i++)
15         month2[i] = s[i%10] + s[i/10];   // 第几月所需笔画数,12月是1和2所需笔画数之和
16     memset(year, 0, sizeof(year));
17     for (int i=0; i<3000; i++)
18     {
19         int m = i;
20         for (int j=0; j<4; j++)
21         {
22             year[i] += s[m%10];  // 第几年所需笔画数2015年是数字2,0,1,5所需笔画数之和
23             m /= 10;  
24         }
25     }
26 }
27 int main ()
28 {
29     init();
30     int t, y, m, d, num, ans;
31 
32     scanf ("%d", &t);
33     while (t --)
34     {
35         int flag = 0;
36         ans = 0;
37         scanf ("%4d%2d%2d", &y, &m, &d);
38 
39         if (y%400==0 || (y%4==0)&&(y%100!=0))   // 是否闰年
40             flag = 1;   
41         scanf ("%d", &num);
42         while (true)
43         {
44             int nu = year[y] + month2[m] + day[d];  // 当前所到日期所需笔画数
45             if (nu == num) // 相等结束while
46                 break;
47             if (y==2999 && m==12 && d==31)
48             {
49                 ans = -1;  
50                 break;
51             }
52             d ++;  // 每次d+1,ans+1,当加到日期笔画数和输入相同时结束while
53             ans ++;
54             if (month1[flag][m] < d) 
55             {
56                 d -= month1[flag][m];  // 月份< d时,天数从0开始下月计算,月份m+1
57                 m ++;
58             } 
59             if (m > 12)  // 月份大于12,下一年开始计
60             {
61                 m -= 12;
62                 y ++;
63                 if (y%400==0 || (y%4==0)&&(y%100!=0))
64                     flag = 1;
65                 else
66                     flag = 0;
67             }
68         }
69         printf ("%d\n", ans);  // 输出天数
70     }
71     return 0;
72 }
C++