puzzle(0412)日历拼图

目录

一,规则

二,每日拼图

2022年2月

2022年3月

竖条下滑问题

2022年4月

2022年5月

2022年6月

三,术语

四,启发式搜索策略

1,数独

2,策略一

3,策略二

4,策略三

5,策略四

6,策略五

7,策略六

五,数字化

1,读取图片并二值化

2,边缘检测

3,轮廓检测

4,求解格子尺寸、坐标

5,计算有效轮廓数量

6,坐标微调

7,手动删减轮廓

8,解析空出来的3个格子

8,连通性计算

9,完整代码

六,以解生解

1,大拇指

2,可视化

3,U型

4,新解

5,日期汇总

6,完整代码V1

7,BUG修复

8,日期优化(完整代码V2)

9,BUG修复、新增检查(完整代码V3)

七,说明

八,直接求解

1,可视化V2版

2,直接求解


一,规则

每天根据月、日、星期去掉3个格子,剩下的格子刚好全部覆盖。

日历拼图有两千多组合,如果所有组合都能拼的话,那真的太神奇了。

二,每日拼图

2022年2月

根据下面的数字化的方法,2022年2月1日-2月28日的答案分别是:

2月1日周二
  1  0  1  8  2  2  0
  1  1  1  8  8  2  0
  0  3  3  9  8  2  2
 10  3  3  9  9  9  5
 10  4  3  5  5  5  5
 10  4  4  4  6  6  6
 10  4  7  7  7  0  6
  0  0  0  0  7  7  6
2月2日周三
  1  0  1  2  3  3  0
  1  1  1  2  2  3  0
  4  0  9  8  2  3  3
  4  9  9  8  2  5  5
  4  9  6  8 10  5  5
  4  4  6  8 10  7  5
  6  6  6 10 10  7  0
  0  0  0  0  7  7  7
2月3日周四
  1  0  1  2  3  3  0
  1  1  1  2  2  3  0
  4  4  0  2  2  3  3
  8  4  4  4  5  5  5
  8  6  9  9  9  5  7
  8  6  9 10 10  5  7
  8  6  6  6 10 10  7
  0  0  0  0  0  7  7
2月4日周五
 10 0 10  7  1  1  0
 10  10 10  7  7  1  0
  2  2  2  0 7  1  1
  2  4  8  8  8  3  3
  2  4  8  3  3  3  9
  4  4  4  6  5  5  9
  6  6  6  6  5  5  9
  0  0  0  0  5  0  9
2月5日周六
  1  0  2  2  3  3  0
  1  1  2  4  4  3  0
  8  1  2  4  0  3  3
  8  1  2  4  4 10  5
  8  6  9 10 10 10  5
  8  6  9  9  5  5  5
  6  6  6  9  7  7  7
  0  0  0  0  7  7  0
2月6日周日
  1  0  1  3  3  8  0
  1  1  1  3  8  8  0
  2  2  3  3  8  0  5
  9  2  4  4  5  5  5
  9  2 10  4  4  4  5
  9  2 10  6  6  6  7
  9 10 10  0  6  6  7
  0  0  0  0  7  7  7
2月7日周一
  1  0  1  2  2  2  0
  1  1  1  2  3  3  0
  8  8  8  2  4  3  0
  8 10 10  4  4  3  9
 10 10  5  4  4  3  9
  5  5  5  6  6  7  9
  5  6  6  6  0  7  9
  0  0  0  0  7  7  7
2月8日周二
  1  0  1  2  3  3  0
  1  1  1  2  2  3  0
  4  4  4  2  2  3  3
  0  4  8  5  5  5  5
  6  4  8  8  8  7  5
  6  9  9  9  9  7  7
  6  6  6 10 10  0  7
  0  0  0  0 10 10  7
2月9日周三
  1  0  1  2  3  3  0
  1  1  1  2  2  3  0
  4  4  4  8  2  3  3
  4  0  6  8  2  5  5
  4  9  6  8 10  5  5
  9  9  6  8 10  7  5
  9  6  6 10 10  7  0
  0  0  0  0  7  7  7
2月10日周四
  1  0  1  2  3  3  0
  1  1  1  2  2  3  0
  4  4  4  2  2  3  3
  5  4  0  6  6  6  8
  5  4  6  6  8  8  8
  5  5  5  7  7  7  7
  9  9  9  9 10 10  7
  0  0  0  0  0 10 10
2月11日周五
  1  0  1  8  2  2  0
  1  1  1  8  8  2  0
  9  3  3  3  8  2  2
  9  7  3  0  4  4  4
  9  7  3  6  6  5  4
  9  7  6  6  6  5  4
  7  7 10 10 10  5  5
  0  0  0  0 10  0  5

2月12日周六
  1  0  1  2  3  3  0
  1  1  1  2  2  3  0
  4  4  4  2  2  3  3
  8  4  5  5  0  6  6
  8  4  9  5  5  5  6
  8  9  9  7  7  7  6
  8  9 10 10 10  7  6
  0  0  0  0 10  7  0
2月13日周日
  1  0  1  2  3  3  0
  1  1  1  2  2  3  0
  4  4  4  4  2  3  3
  8  9  9  4  2  0  5
  8 10  9  9  5  5  5
  8 10  6  6  6  7  5
  8 10 10  0  6  7  7
  0  0  0  0  6  7  7
2月14日周一
  1  0  1  4  2  2  0
  1  1  1  4  7  2  0
  3  4  4  4  7  2  2
  3  3  3  3  7  5  0
  8  6  6  6  7  5  5
  8  6  6 10 10  9  5
  8  8 10 10 0 9  5
  0  0  0  0  9  9  9
2月15日周二
  7  0  1  8  8  8  0
  7  1  1  2  9  8  0
  7  1  1  2  9  9  3
  7  4  2  2  2  9  3
  0  4 10 10  3  3  3
  4  4 10 5  5  5  5
  4 10 10 5  6  0  6
  0  0  0  0  6  6  6
2月16日周三
  1  0  1  5  2  3  0
  1  1  1  5  2  3  0
  4  5  5  5  2  3  3
  4  4  4  6  2  7  3
  4  0  9  6  6  7  7
  9  9  9  6  6 10  7
  9  8  8  8  8 10 0
  0  0  0  0  8 10 10

2月17日周四
  1  0  2  2  2  3  0
  1  1  6  6  2  3  0
  4  1  6  5  2  3  3
  4  6  6  5  5  5  7
  4  4  0  5 10  8  7
  9  4  9 10 10  8  7
  9  9  9 10 10  8  7
  0  0  0  0  0  8  8
2月18日周五
  1  0  2  2  2  3  0
  1  2  2  3  3  3  0
  1  1  1  4  4  4  4
  5  5  5  5  8  8  6
  5  7  9  0  8  6  6
  7  7  9  8  8  6  6
  7  9  9  9 10 10 10
  0  0  0  0 10  0 10
2月19日周六
  1  0  1  10  10  10  0
  1  1  1  2  2  10  0
  3  4  4  2  2  10  6
  3  5  4  2  6  6  6
  3  5  4  4  0  7  6
  3  5  5  8  7  7  9
  8  8  8  8  7  9  9
  0  0  0  0  7  9  0
2月20日周日
  1  0  1  2  2  2  0
  1  1  1  3  2  7  0
  3  3  3  3  2  7  7
  4  5  5  5  6  6  7
  4  5  8  6  6  0  7
  4  5  8  10  10  9  9
  4  8  8  0  10  9  9
  0  0  0  0  10  10  9
2月21日周一
  1  0  1  2  2  2  0
  1  1  1  4  2  3  0
  4  4  4  4  2  3  3
  5  6  6  6  7  7  3
  5  6  8  8  8  7  0
  5  6  8  9  9  7  7
  5  9  9  9  0 10 10
  0  0  0  0 10 10 10
2月22日周二
  1  0  2  2  3  3  0
  1  1  1  2  4  3  0
  1  5  5  2  4  3  3
  5  5  6  6  4  4  4
  9  9  6  6  6  8  7
  0  9  8  8  8  8  7
  9  9 10 10 10  0  7
  0  0  0  0 10 10  7
2月23日周三
  1  0  1  2  3  3  0
  1  1  1  2  2  3  0
  4  4  4  4  2  3  3
  5  5  5  5  2  6  6
  7  7  7  5  9  6  6
  7  0  8  8  9 10  6
  7  8  8  9  9 10  0
  0  0  0  0 10 10 10
2月24日周四
  1  0  1  2  2  2  0
  1  1  1  5  3  2  0
  4  5  5  5  3  3  7
  4  5  6  6  6  3  7
  4  9  6  6  7  7  7
  4  9  0  8  8  8  8
  9  9  9 10 10 10  8
  0  0  0  0  0 10 10
2月25日周五
  2  0  1  1  1  1  0
  2  2  2  3  3  3  0
  2  5  5  3  4  4  4
  5  5  6  6  6  6  4
  7  8  8  9  9  6  4
  7  7  8 0  9  9  9
  7  7  8  8 10 10 10
  0  0  0  0 10  0 10
2月26日周六
  1  0  1  2  2  2  0
  1  1  1  3  2  4  0
  5  6  6  3  2  4  4
  5  7  6  3  3  3  4
  5  7  6  6  8  8  4
  5  7  7  9  0  8  8
  9  9  9  9 10 10 10
  0  0  0  0 10 10  0
2月27日周日
  1  0  1  5  5  2  0
  1  1  1  5  2  2  0
  3  4  5  5  2  2  7
  3  4  4  6  7  7  7
  3  9  4  6  6  6  6
  3  9  4  8  8  0 10
  9  9  9  0  8  8 10
  0  0  0  0 10 10 10
2月28日周一
  1  0  1  2  2  2  0
  1  1  1  5  2  3  0
  4  5  5  5  2  3  3
  4  5  6  6  7  7  3
  4  4  4  6  6  7  3
  8  9  9  9  9  7  0
  8  8  8  8  0 10 10
  0  0  0  0 10 10 10

拼的时候经常会出现,还有一块拼不上,而空出来的格子组成的是另一个块的形状的情况,如:

  

 

2月22日周二

拼的时候再次出现这种情况:

稍微调整就能得到:

2022年3月

2022年3月1日-3月31日的答案分别是:

3月1日周二
  1  1  0  2  2  2  0
  1  1  2  2  3  3  0
  0  1  6  4  4  3  5
  6  6  6  7  4  3  5
  6  8  9  7  4  3  5
  8  8  9  7  7  7  5
  8  9  9  9 10  0 10
  0  0  0  0 10 10 10
3月2日周三
  1  2  0  3  4  4  0
  1  2  2  3  3  4  0
  1  0  2  3  3  4  4
  1  1  2  5  5  5  5
  6  7  7  7  8  8  8
  6  7  9  9  8 10  8
  6  6  6  9  9 10  0
  0  0  0  0 10 10 10
3月3日周四
  1  2  0  2  5  5  0
  1  2  2  2  5  3  0
  1  4  0  5  5  3  3
  1  4  4  4  4  7  3
  6  6  6  7  7  7  9
  6  8  8  8  9  9  9
  6  8  8 10 10 10  9
  0  0  0  0  0 10 10
3月4日周五
  1  1  0  10  10  10  0
  3  1  1  1  2  10  0
  3  3  3  0  2  10  7
  3  4  6  6  2  2  7
  4  4  6  5  5  5  7
  4  6  6  5  5  7  7
  8  8  8  8  9  9  9
  0  0  0  0  9  0  9
3月5日周六
  1  2  0  2  3  3  0
  1  2  2  2  3  3  0
  1  4  4  4  0  3  9
  1  5  4  6  6  6  9
  7  5  4  6  8  8  9
  7  5  5  6  8  9  9
  7  7  5 8 8 10 10
  0  0  0  0 10 10  0
3月6日周日
  1  2  0  2  3  3  0
  1  2  2  2  3  3  0
  1  1  4  4  3  0  7
  5  1  4  6  7  7  7
  5  8  4  6  6  6  7
  5  8  4 10 10  6 9
  5  8  8 0 10 10 9
  0  0  0  0 9 9 9
3月7日周一
  1  1  0  2  2  2  0
  1  1  2  2  3  3  0
  1  5  4  4  4  3  0
  5  5  4  6  4  3  3
  5  6  6  6  7  7  7
  8  9  9  9  9 10  7
  8  8  8  8  0 10  7
  0  0  0  0 10 10 10
3月8日周二
  1  1  0  2  3  3  0
  1  1  1  2  2  3  0
  4  4  4  4  2  3  3
  0  5  5  5  6  6  6
  7  5  8  8  8  8  6
  7  7  7  9  9  8  6
  7  9  9  9 10  0 10
  0  0  0  0 10 10 10

3月9日周三
  1  1  0  2  2  3  0
  1  2  2  2  3  3  0
  1  1  7  7  3  3  4
  5  0  7  6  6  6  4
  5  7  7  6  8  8  4
  5  9  9  6  8 10  4
  5  5  9  9  8 10  0
  0  0  0  0 10 10 10
3月10日周四
  1  1  0  2  2  2  0
  3  1  1  2  5  5  0
  3  3  3  2  5  9  4
  3  6  0  5  5  9  4
  6  6  7  7  7  9  4
  6  8  7  8  9  9  4
  6  8  8  8 10 10 10
  0  0  0  0  0 10 10
3月11日周五
  1  1  0  2  3  3  0
  4  1  1  2  2  3  0
  4  4  4  2  2  3  3
  4  7  5  0  6  6  6
  7  7  5  5  5  5  6
  7  8  9  9  9  9  6
  7  8  8  8 10 10 10
  0  0  0  0 10  0 10
3月12日周六
  1  2  0  2  3  3  0
  1  2  2  2  5  3  0
  1  4  5  5  5  3  3
  1  4  4  6  0  8  8
  7  4  4  6  8  8 10
  7  7  7  6  6  6 10
  7  9  9  9  9 10 10
  0  0  0  0  9 10  0
3月13日周日
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  1  4  4  4  3  3
  5  5  5  5  4  0  7
  5  6  6  6  6  7  7
  8  8  9  9  9  7 10
  8  8  8  0  9  9 10
  0  0  0  0 10 10 10
3月14日周一
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  1  1  4  4  3  3
  5  6  6  4  9  7  0
  5  6  8  9  9  7  7
  5  6  8  9  9 10  7
  5  6  8  8  0 10  7
  0  0  0  0 10 10 10
3月15日周二
  1  1  0  2  3  3  0
  1  1  1  2  3  4  0
  5  5  5  2  3  4  4
  5  7  5  2  3  6  4
  0  7  9  6  6  6  8
  7  7  9  6  8  8  8
  9  9  9 10 10  0  8
  0  0  0  0 10 10 10
3月16日周三
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  1  1  4  4  3  3
  5  5  8  4  6  7  7
  5  0  8  9  6  7  7
  5  8  8  9  6 10  7
  5  8  9  9  6 10  0
  0  0  0  0 10 10 10
3月17日周四
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  1  7  5  4  3  3
  6  7  7  5  4  4  4
  6  7  0  5  5  8  8
  6  9  9  9  5 10  8
  6  9  9 10 10 10  8
  0  0  0  0  0 10  8

3月18日周六
  1  2  0  6  3  3  0
  1  2  2  6  4  3  0
  1  5  2  6  4  3  3
  1  5  6  6  4  4  4
  7  5  5  0  8  8  8
  7  7  7  9  9  8  8
  7  9  9  9 10 10 10
  0  0  0  0 10  0 10
3月19日周六
  1  2  0  2  3  3  0
  1  2  2  2  5  3  0
  1  4  5  5  5  3  3
  1  4  4  6  5  7  7
  8  8  4  6  0  7  7
  8  9  4  6  6  6  7
  8  9  9  9  9 10 10
  0  0  0  0 10 10  0
3月20日周日
  1  2  0  2  3  3  0
  1  2  2  2  5  3  0
  1  4  4  5  5  3  3
  1  4  10 5  8  8  8
  7  4  10 8  8  0 6
  7  4 10 10 6 6 6
  7  7  7  0 9 9 6
  0  0  0  0 9 9 9
3月21日周一
  1  2  0  2  3  3  0
  1  2  2  2  5  3  0
  1  4  4  5  5  3  3
  1  1  4  5  7  7  7
  6  6  4  7  7  8  0
  6  6  6  8  8  8 10
  9  9  9  9  0  8 10
  0  0  0  0 10 10 10
3月22日周二
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  1  1  4  4  3  3
  5  5  5  4  8  8  6
  5  9  7  8  8  8  6
  0  9  7  7  7  7  6
  9  9  9 10 10  0  6
  0  0  0  0 10 10 10
3月23日周三
  1  2  0  3  4  4  0
  1  2  2  3  3  4  0
  1  1  2  3  3  4  4
  5  5  5  5  6  6  6
  5  7  7  7  7  9  6
  8  0  8  9  9  9  6
  8  8  8 10 10  9  0
  0  0  0  0 10 10 10
3月24日周四
  1  2  0  2  3  3  0
  1  2  2  2  5  3  0
  1  4  4  4  5  3  3
  1  4  5  5  5  7  7
  6  6  6  6  7  7  9
  8  8  0  6  9  9  9
  8  8  8 10 10 10  9
  0  0  0  0  0 10 10
3月25日周五
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  7  5  5  4  3  3
  1  7  5  6  4  4  4
  7  7  5  6  6  6  6
  7  9  9  0 10  8  8
  9  9 10 10 10  8  8
  0  0  0  0 10  0  8
3月26日周六
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  1  4  4  4  3  3
  5  5  5  5  4  6  6
  7  8  8  9  9  9  6
  7  7  8  8  0  9  6
  7  7 10 10 10  9  6
  0  0  0  0 10 10  0
3月27日周日
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  1  6  7  4  3  3
  5  6  6  7  4  4  4
  5  6  7  7  7  8  8
  5  6 10 10  9  0  8
  5 10 10  0  9  9  8
  0  0  0  0  9  9  8
3月28日周一
  1  2  0  3  3  3  0
  1  2  2  2  2  3  0
  1  1  4  5  5  3  7
  6  1  4  5  7  7  7
  6  6  4  5  5  9  7
  8  6  4  9  9  9  0
  8  8  8  9  0 10 10
  0  0  0  0 10 10 10
3月29日周二
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  5  5  5  4  3  3
  1  7  6  5  4  4  4
  7  7  6  6  6  6  9
  7  8  8  8  9  9  9
  0  8  8 10 10  0  9
  0  0  0  0 10 10 10
3月30日周三
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  5  5  6  4  3  3
  1  5  6  6  4  4  4
  7  5  6  8  8  8  8
  7  7  7  9  9  9  8
  7  0  9  9 10 10  0
  0  0  0  0 10 10 10

3月31日周四
  1  2  0  2  3  3  0
  1  2  2  2  4  3  0
  1  4  4  4  4  3  3
  1  5  5  5  6  6  6
  7  7  8  5  6  6  9
  7  8  8  5  9  9  9
  7  8  0 10 10 10  9
  0  0  0  0  0 10 10

竖条下滑问题

因为是平放在桌面上拼的,所以经常忘了竖条下面不能放空格,然后每次我都会重新拼。

2月5日周六

 

3月9日周三

4月2日周六

2022年4月

4月1日周五
  1  1  2  0  3  3  0
  1  1  2  2  2  3  0
  0  1  2  5  5  3  3
  4  5  5  5  6  6  6
  4  8  9  9  9  9  6
  4  8  8 10 10 10  6
  4  4  8 10  7  7  7
  0  0  0  0  7  0  7
4月2日周六
  1  1  2  0  2  3  0
  1  1  2  2  2  3  0
  1  0  4  4  3  3  3
  4  4  4  5  8  8  8
  6  6  6  5  5  5  8
  6  9  9  9  9  5 10
  6  7  7  7  7 10 10
  0  0  0  0  7 10  0
4月3日周日
  1  1  1  0  2  2  0
  8  1  3  3  3  2  0
  8  1  0  3  3  2  2
  8  4  4  4  5  6  6
  8  4  9  9  5  5  6
  7  4  7  9  9  5  6
  7  7  7  0 10  5  6
  0  0  0  0 10 10 10

4月4日周一
  6  6  1  0  1  2  0
 7  6  1  1  1  2  0
  7  6  6 0  2  2  2
  7  7  7  4  4  8  9
  3  4  4  4  8  8  9
  3  3  3  3  8  9  9
 10 10 10 10  0  5  5
  0  0  0  0  5  5  5
4月5日周二
  8  1  1  0  2  2  0
  8  3  1  1  1  2  0
  8  3  3  3  0  2  2
  8  3 10 10  4  4  5
  9 10 10  4  4  4  5
  9  9  9  6  5  5  5
  6  6  6  6  7  0  7
  0  0  0  0  7  7  7
4月6日周三
  1  1  1  0  3  3  0
  1  2  2  3  3  3  0
  1  4  2  2  2  0  5
  8  4  4  4  5  5  5
  8  9  9  4  6  6  5
  8  8  9  9  6  7  7
 10 10 10 10  6  7  0
  0  0  0  0  6  7  7

4月7日周四
  1  1  2  0  2  4  0
  3  1  2  2  2  4  0
  3  1  1  4  4  4  0
  3  3  5  5  5  8  8
  6  3  5  5  8  8  7
  6  6  6  9  9  9  7
  6 10 10 10 10  9  7
  0  0  0  0  0  7  7
4月8日周五
  1  1  2  0  2  4  0
  1  1  2  2  2  4  0
  1  5  5  3  4  4  4
  0  5  7  3  3  3  3
  5  5  7  6  8  8  8
  7  7  7  6  6  9  8
 10 10 10 10  6  9  9
  0  0  0  0  6  0  9

4月9日周六
  1  1  2  0  2  4  0
  1  3  2  2  2  4  0
  1  3  3  3  4  4  4
  1  0  8  3  6  6  6
  5  8  8  6  6  9  9
  5  8 10 10 10 10  9
  5  5  5  7  7  7  9
  0  0  0  0  7  7  0

4月10日周日
  1  1  2  0  2  4  0
  1  3  2  2  2  4  0
  1  3  3  3  4  4  4
  1  6  0  3  5  5  5
  6  6  8  8  8  8  5
  6  7  7  7  9  9  5
  6  7  7  0 10  9  9
  0  0  0  0 10 10 10

4月11日周一
  1  1  2  0  3  3  0
  1  1  2  2  2  3  0
  7  1  2 10  10  3  3
  7  7  4 0 10 10 10
  5  7  4  4  4  4  8
  5  9  9  9  6  6  8
  5  5  5  9  0  6  8
  0  0  0  0  6  6  8
4月12日周二
  1  1  2  0  3  3  0
  1  1  2  2  2  3  0
  4  1  2  5  5  3  3
  4  5  5  5  0  8  8
  4  4  4  6  8  8  9
  6  6  6  6  9  9  9
 10 10 10 10  7  0  7
  0  0  0  0  7  7  7
4月13日周三
  1  1  2  0  2  3  0
  8  1  2  2  2  3  0
  8  1  1  5  4  3  3
  8  5  5  5  4  0  3
  8 10 10  5  4  4  4
 10 10  9  6  6  6  6
  9  9  9  6  7  7  0
  0  0  0  0  7  7  7

4月14日周四
  1  1  1  0  2  2  0
  1  3  3  3  4  2  0
  1  7  3  8  4  2  2
  5  7  3  8  4  4  0
  5  7  7  8  6  4  6
  5  9  9  8  6  6  6
  5  5  9  9 10 10 10
  0  0  0  0 10 10  10
4月15日周五
  1  1  8  0  2  2  0
  1  3  8  8  4  2  0
  1  3  3  8  4  2  2
  1  6  3  9  4  4  4
  0  6  3  9  5  5  5
  6  6  6  9  9  5  5
 10 10 10 10  7  7  7
  0  0  0  0  7  0  7

4月16日周六
  1  1  2  0  2  4  0
  1  3  2  2  2  4  0
  1  3  3  3  4  4  4
  1  5  5  3  8  8  8
  6  0  5  5  5  9  8
  6 10 10 10 10  9  9
  6  6  6  7  7  7  9
  0  0  0  0  7  7  0

4月17日周日
  8  1  2  0  2  3  0
  8  1  2  2  2  3  0
  8  1  9  9  3  3  3
  8  1  1  9  9  5  5
  4 10  0  5  5  5  6
  4 10 10 10  6  6  6
  4  4  4  0  6  7  7
  0  0  0  0  7  7  7
4月18日周一
  1  1  2  0  2  3  0
 10  1  2  2  2  3  0
 10  1  1  3  3  3  4
 10  10  7  4  4  4  4
  5 10  7 0  9  8  8
  5  5  7  9  9  6  8
  5  5  7  9  0  6  8
  0  0  0  0  6  6  6

4月19日周二
  1  1  2  0  2  4  0
  1  3  2  2  2  4  0
  1  3  3  3  4  4  4
  1  5  5  3  8  8  8
  6  5  5  5  0  7  8
  6  9  9  9  9  7  7
  6  6  6 10 10  0  7
  0  0  0  0 10 10  7

4月20日周三
  1  1  2  0  2 10  0
  1  7  2  2  2  10  0
  1  7  7  7 10 10 10
  1  8  9  9  9  9  5
  3  8  8  6  6 0  5
  3  3  8  6  4  5  5
  3  3  6  6  4  5  0
  0  0  0  0  4  4  4
4月21日周四
  7  1  2  0  2  3  0
  7  1  2  2  2  3  0
  7  1  1  1  4  3  3
  7  4  4  4  4 10  3
  8  8  9  5  5  10 0
  8  9  9  5 10 10 10
  8  9  5  5  6  6  6
  0  0  0  0  0  6  6

4月22日周五
  1  2  2  0  3  3  0
  1  2  2  2  4  3  0
  1  1  5  5  4  3  3
  7  1  5  6  4  4  4
  7  7  5  6  6  6  8
  0  7  5  6  8  8  8
  9  9  9  9 10 10 10
  0  0  0  0 10  0 10

4月23日周六
  8  1  2  0  2  3  0
  8  1  2  2  2  3  0
  8  1  1  1  3  3  3
  8  5  7  4  9  9  9
  5  5  7  4  4  4  9
  5  0  7  6  6  4 10
  5  7  7  6  6 10 10
  0  0  0  0  6 10  0

4月24日周日
  1  2  3  0  3  4  0
  1  2  3  3  3  4  0
  1  2  2  2  4  4  4
  1  1  5  5  5  8  8
  9  9  9  9  5  5  8
  6  6  0  7  7 10  8
  6  6  6  0  7 10 10
  0  0  0  0  7  7 10

4月25日周一
  8  1  1  0  2  2  0
  8  8  1  1  1  2  0
  9  8  3  3  3  2  2
  9  6  3  3  4  4  4
  9  6  5  5  4 10 10
  9  6  5  0  4  7 10
  6  6  5  5  0  7 10
  0  0  0  0  7  7  7

4月26日周二
  1  1  2  0  3  3  0
  1  1  2  2  2  3  0
  4  1  2  5  5  3  3
  4  5  5  5  8  9  9
  4  4  4  6  8  8  9
  6  6  6  6  0  8  9
 10 10 10 10  7  0  7
  0  0  0  0  7  7  7

4月27日周三
  8  8  1  0  1  2  0
  9  8  1  1  1  2  0
  9  8 10 10  2  2  2
  9 10 10  4  3  3  3
  9  4  4  4  6  6  3
  5  4  6  6  6  0  3
  5  5  5  5  7  7  0
  0  0  0  0  7  7  7

4月28日周四
  1  1  2  0  2  5  0
  3  1  2  2  2  5  0
  3  1  1  4  5  5  5
  3  3  3  4  4  4  4
  8  8 10  9  9  9  9
  8 10 10  6  6  6  0
  8 10  6  6  7  7  7
  0  0  0  0  0  7  7

4月29日周五
  1  1  1  0  2  2  0
  1  3  3  3  3  2  0
  1  9  9  8  3  2  2
  9  9  4  8  5  5  5
  4  4  4  8 10  5  5
  6  6  4  8 10 10 10
  0  6  6  6  7  7  7
  0  0  0  0  7  0  7
4月30日周六
  1  1  2  0  2  5  0
  1  1  2  2  2  5  0
  1  3  3  4  5  5  5
  8  6  3  4  4  4  4
  8  6  3  3  9  9  9
  8  6  6  6  7  7  9
  8  0  7  7  7 10 10
  0  0  0  0 10 10  0

2022年5月

5月1日周日
  1  1  1  2  0  2  0
  1  1  8  2  2  2  0
  0  4  8  9  9  3  3
  4  4  8 10  9  9  3
  4  7  8 10  5  5  3
  4  7 10 10  6  5  3
  7  7  7  0  6  5  5
  0  0  0  0  6  6  6

5月2日周一
  1  1  2  3  0  3  0
  1  1  2  3  3  3  0
  1  0  2  2  2  6  6
  4  4  5  6  6  6  8
  9  4  5  5  5  5  8
  9  4  4 10 10  7  8
  9  9 10 10  0  7  8
  0  0  0  0  7  7  7
5月3日周二
  1  1  1  2  0  2  0
  1  4  4  2  2  2  0
  1  4  0  3  8  8  5
  4  4  6  3  8  5  5
  6  6  6  3  8  5  9
  7  7  6  3  3  5  9
  7  7  7 10 10  0  9
  0  0  0  0 10 10  9

 5月4日周三
  8  8  8  8  0  1  0
  2  2  3  3  3  1  0
  2  4  3  0  3  1  1
  2  4  4  4 10  9  1
  2  6  5  4 10  9  9
  6  6  5 10 10  7  9
  6  6  5  5  5  7  0
  0  0  0  0  7  7  7
5月5日周四
  8  1  1  2  0  2  0
  8  3  1  2  2  2  0
  8  3  1  1  0  4  4
  8  3  3  3  9  4  4
  5  5  9  9  9  4  7
  6  5  5  5  7  7  7
  6  6  6  6 10 10  7
  0  0  0  0  0 10 10

5月6日周五
  1  1  1  2  0  2  0
  1  3  3  2  2  2  0
  1  4  3  3  3  0  5
  8  4  4  4  5  5  5
  8  9  9  4  6  6  5
  8 10  9  9  6  7  7
  8 10 10 10  6  7  7
  0  0  0  0  6  0  7

5月7日周六
  1  1  1  1  0  2  0
  1  9  8  2  2  2  0
  9  9  8  3  3  2  0
  9  4  8  3  5  5  5
 10  4  8  3  3  6  5
 10  4  4  6  6  6  5
 10 10  4  6  7  7  7
  0  0  0  0  7  7  0

5月8日周日
  1  1  1  1  0  2  0
  1  3  3  3  5  2  0
  4  4  3  5  5  2  2
  0  4  3  5  5  8  2
  6  4  4  8  8  8 10
  6  9  9  9  9 10 10
  6  6  6  0  7 10  7
  0  0  0  0  7  7  7

5月9日周一
  1  2  2  3  0  3  0
  1  1  2  3  3  3  0
  1  1  2  2  5  5  5
  8  0  4  5  5 10  9
  8  8  4 10 10 10  9
  6  8  4  4  4  7  9
  6  6  6  6  0  7  9
  0  0  0  0  7  7  7

5月10日周二
  1  1  1  2  0  2  0
  1  3  3  2  2  2  0
  1  3  8  8  8  8  4
  3  3  0  5  4  4  4
  5  5  5  5 10  9  4
  6  6 10 10 10  9  9
  6  6  6  7  7  0  9
  0  0  0  0  7  7  7

5月11日周三
  1  1  1  2  0  2  0
  1  1  3  2  2  2  0
  8  8  3  3  3  4  4
  5  8  8  0  3  6  4
  5  9  9  9  6  6  4
  5  5  5  9  6  7  4
 10 10 10 10  6  7  0
  0  0  0  0  7  7  7

5月12日周四
  1  1  2  3  0  3  0
  1  1  2  3  3  3  0
  8  1  2  2  4  4  4
  8  8  8  2  0  4  9
  5  5  5 10 10  4  9
  5  6 10 10  7  7  9
  5  6  6  6  6  7  9
  0  0  0  0  0  7  7

5月13日周五
  8  8  8  8  0  1  0
  9  9  2  2  2  1  0
 10  9  9  2  3  1  1
 10 10 10  2  3  0  1
  4  4  6  6  3  3  3
  4  4  6  5  5  5  5
  4  6  6  5  7  7  7
  0  0  0  0  7  0  7

5月14日周六
  1  1  1  2  0  2  0
  1  3  3  2  2  2  0
  1  3  3  8  8  4  4
  9  3 10 10  8  4  0
  9 10 10  5  8  4  7
  9  5  5  5  6  4  7
  9  5  6  6  6  7  7
  0  0  0  0  6  7  0

5月15日周日
  8  8  8  1  0  1  0
  2  9  8  1  1  1  0
  2  9  9 10 10 10 10
  2  2  9  3  3  3  3
  0  2  4  3  5  5  5
  6  6  4  4  4  7  5
  6  6  6  0  4  7  5
  0  0  0  0  7  7  7
5月16日周一
  1  1  1  2  0  2  0
  1  3  3  2  2  2  0
  1  8  3  4  4  5  5
  8  8  3  4  4  5  9
  8  0  3  4  5  5  9
 10 10 10  6  6  7  9
 10  6  6  6  0  7  9
  0  0  0  0  7  7  7

5月17日周二
  1  1  1  2  0  2  0
  1  8  8  2  2  2  0
  1  3  8  8  9  9  9
 10  3  3  3  3  5  9
 10  4  0  5  5  5  6
 10  4  4  5  6  6  6
 10  4  4  7  7  0  6
  0  0  0  0  7  7  7

5月18日周三
  1  1  1  2  0  2  0
  8  8  1  2  2  2  0
  9  8  1  3  3  4  4
  9  8 10  3  3  3  4
  9 10 10  0  6  6  4
  9 10  5  5  6  7  4
  5  5  5  6  6  7  0
  0  0  0  0  7  7  7
5月19日周四
  1  1  1  2  0  2  0
  8  1  4  2  2  2  0
  8  1  4  3  3  3  3
  8  4  4  3  5  5  5
  8  4 10 10  0  5  5
  9 10 10  6  7  7  7
  9  9  9  6  6  6  7
  0  0  0  0  0  6  7
5月20日周五
  8  1  1  1  0  2  0
  8  1  4  4  2  2  0
  8  1  4  5  2  3  3
  8  4  4  5  2  3  3
  9  9  5  5  5  0  3
 10  9  9  6  6  6  6
 10 10 10  6  7  7  7
  0  0  0  0  7  0  7

5月21日周六
  1  1  1  2  0  6  0
  1  2  2  2  3  6  0
  1  2  3  3  3  6  6
  7  7  7  4  3  4  6
  8  8  7  4  4  4 0
 10  8  8  9  9  9  9
 10 10 10 10  5  5  5
  0  0  0  0  5  5  0

5月22日周日
  8  1  9  9  0  2  0
  8  1 10  9  9  2  0
  8  1 10 10 10  2  2
  8  1  1  3  3  3  2
  5  5  4  3  3  6  6
  0  5  4  4  4  6  7
  5  5  4  0  6  6  7
  0  0  0  0  7  7  7

5月23日周一
  8  1  1  2  0  2  0
  8  8  1  2  2  2  0
  3  8  1  1  4  4  4
  3 10 10 10  4  4  9
  3  3  3 10  6  6  9
  5  0  6  6  6  7  9
  5  5  5  5  0  7  9
  0  0  0  0  7  7  7

5月24日周二
  8  1  1  2  0  2  0
  8  3  1  2  2  2  0
  8  3  1  1  9  9 10
  8  3  3  3  9 10 10
  4  4  4  4  9 10  6
  5  5  0  4  6  6  6
  5  5  5  7  7  0  6
  0  0  0  0  7  7  7

5月25日周三
  1  1  1  2  0  2  0
  1  1  3  2  2  2  0
  8  8  3  3  3  4  4
  5  8  8  9  3  6  4
  5  9  9  9  6  6  4
  5  5  5  0  6  7  4
 10 10 10 10  6  7  0
  0  0  0  0  7  7  7

5月26日周四
  8  1  1  2  0  2  0
  8  8  1  2  2  2  0
  3  8  1  1  5  4  4
  3  9  9  9  5  4  4
  3  9  5  5  5  4 10
  3  3  6  6  0  7 10
  6  6  6  7  7  7 10
  0  0  0  0  0  7 10

5月27日周五
  1  1  1  2  0  5  0
  1  2  2  2  3  5  0
  1  2  4  3  3  5  8
  4  4  4  3  5  5  8
  9  9  4  3 10 10  8
  9  6  6 10 10  0  8
  9  6  6  6  7  7  7
  0  0  0  0  7  0  7

5月28日周六
  1  1  1  2  0  2  0
  1  3  3  2  2  2  0
  1  9  3  8  8  8  8
  9  9  3  6 10 10 10
  9  5  3  6  4  4 10
  5  5  6  6  6  4  0
  5  5  7  7  7  4  4
  0  0  0  0  7  7  0

5月29日周日
  1  1  1  2  0  2  0
  3  1  4  2  2  2  0
  3  1  4  4  5  5  5
  3  3  4  4  5  6  6
  8  3  9  9  5  6 10
  8  8  9  7  7  6 10
  0  8  9  0  7  6 10
  0  0  0  0  7  7 10

5月30日周一
  8  1  1  2  0  2  0
  8  3  1  2  2  2  0
  8  3  1  1  9  9  9
  8  3  3  3  5 10  9
  4  5  5  5  5 10 10
  4  4  4  6  6  6 10
  4  0  6  6  0  7  7
  0  0  0  0  7  7  7

5月31日周二
  1  1  1  2  0  2  0
  1  1  3  2  2  2  0
  4  4  3  3  3  8  8
  7  4  3  8  8  8  5
  7  4  4  5  5  5  5
  7  9  9  9 10 10  6
  7  9 0 10 10  0  6
  0  0  0  0  6  6  6

2022年6月

6月1日周三
  1  2  2  3  3  0  0
  1  1  2  3  3  3  0
  0  1  2  2  4  4  4
  8  1  5  5  5  5  4
  8  9  9 10 10  5  4
  8  9 10 10  7  6  6
  8  9  7  7  7  6  0
  0  0  0  0  7  6  6

6月2日周四
  1  1  2  2  2  0  0
  1  1  4  3  2  2  0
  1  0  4  3  3  3  5
  4  4  4  3  5  5  5
  6  6  8  8  5  9  7
  6  8  8  9  9  9  7
  6  6 10 10 10 10  7
  0  0  0  0  0  7  7

6月3日周五
  1  2  3  3  3  0  0
  1  2  2  2  3  3  0
  1  2  0  7  7  9  8
  1  1  5  7  9  9  8
  4  5  5  7  9  6  8
  4  5  5  6  6  6  8
  4  4  4  6 10 10 10
  0  0  0  0 10 0 10

6月4日周六
  8  1  2  2  2  0  0
  8  1  2  4  4  3  0
  8  1  2  0  4  3  3
  8  1  1  4  4  5  3
  9  9 10  5  5  5  3
  9 10 10  5  7  6  6
  9 10  7  7  7  6  6
  0  0  0  0  7  6  0

三,术语

可行的匹配:把某个块放在某个位置之后,如果接下来还有解,那就是可行的匹配。

平坦区域:接近矩形的区域,没有十分明显的局部特征。

四,启发式搜索策略

1,数独

我构造了一个数独来显化这种启发式策略:

对于这个数独,没有任何疑问,首先看行填2,其次看列填5,然后看宫填9。

 现在我们来总结一下这个思维的本质。

有些人可能会这么描述:“先把确定的填了,再看不确定的”,或者“先把简单的填了,再看难的”。

这些说法没错,但是不够精确。

在一开始,单独看3个格子中的任意一个的话,E2有3种情况(看宫),分别是259,F2有2种情况(看列),分别是25,F3有1种情况(看行),是2

那么,在这个深度优先搜索问题中,我们的策略是,先把元素进行排序,情况少的元素往前排,即先搜索情况比较少的元素。这是深度优先搜索的常见优化技巧。

2,策略一

如果对于结构比较复杂的局部区域,有某种拼法是比较精巧的,那这就很可能是某个正确答案的一部分

  

这样一条模糊的策略,是否正确?本质是啥?

首先,结构比较复杂的局部区域是问题的一种特征,精巧的覆盖是解的一种特征

问题的特征告诉我们,这个位置的可能的情况比较少,而平坦位置的可能情况比较多。

解的特征告诉我们,有相对较高的概率这个一个可行的匹配,如果确实是一个可行匹配,那么在此匹配前提下的解是相对较多的。

也就是说,这其实暗含了下面列的很多条策略,没想到吧O(∩_∩)O哈哈~

3,策略二

先从结构比较复杂的局部区域开始拼,平坦的区域靠后拼

此时我们和上面的数独对比,可以感受到,他们的核心逻辑是完成相同的。

通常情况下,被排除的3个格子、右上角、右下角都是结构比较复杂的局部区域。

4,策略三

我们在拼的时候经常需要评估,一个匹配的优劣程度。在面临选择时,我们优先选择直觉上更优的匹配

直觉有点玄乎,但是我们可以看到我在上面给出的三月的解法有很多都是这样的:

可以说这就是很优的匹配。

PS:如果我一早就想通了这个策略,3月至少有20天这2个块就可以这么放。

对于4月,我相信这是一个不错的匹配:

5,策略四

先从复杂的块开始拼,简单的块靠后

这个和策略二很相似,策略二其实是,复杂的局部区域有更少的可能性,策略四其实是,复杂的块有更少的可能性。

以下图为例感受一下简单块的可能性之多:

 即使到了最后一步,仍然有2种放法。

而如果留到最后的是复杂的块,就很可能是0-1种放法。

根据块的复杂程度,我简单分成三个梯度,复杂,中等,简单。

3个复杂的:

5个中等的:

2个简单的:

6,策略五

首先我们分析一下这个拼图的平直程度。

例子太多,我不一一列举。

我直接给出我总结出的规律,在同类puzzle中,日历拼图的平直程度是非常高的

 为什么会这样呢?我认为主要是块本身的属性造成的。

我把日历拼图的10个块,在方格游戏中的17个格子中标示出来(去掉小于4个格子的只有17个)

1、3、4、7、11、14、16号这7个是没有的,而其中的1、3、4、7、14显然符合我在上面策略四中提到的“复杂的块”这个特征。

从某种程度上说,这个复杂其实说的就是不平直的程度。

日历拼图和方格游戏是两个极端日历拼图为了每个组合都有解,选的主要是平直度较高的块,方格游戏其实不是覆盖问题,而是依赖角点进行区域拓宽的一种博弈游戏,所以选的主要是平直度较低的块

上面的策略四是根据块的复杂性(平直度)进行排序,而这里的策略五只针对平直度低的块,以3月29日周二为例:

周二这个格子和L型的块是一个很差的匹配。

而这是一个非常好的匹配,这个块和边界完美贴合,和周二的匹配也非常好。

综上,策略五就是,月日周排除的三个格子,尤其是离边界还有一个距离的格子(如上周二),要放在块的折口处,但是不能放L型块的折口处,从而维持整个局面的平直性

7,策略六

策略六是替换策略。

以2月4日周五为例,我拼成了这样:

乍一看无解,实际上答案已经出来了:

一般来说,最简单的替换是这2组:

五,数字化

利用边缘检测和其他图像处理技术,把一张包含答案的图片,转化成数字。

1,读取图片并二值化

Mat GetImage(int i)
{
	Mat img = imread("D:/p/img (" + to_string(i) + ").png", 0);
	resize(img, img, Size(0, 0), 0.5, 0.5);
	Mat src;
	threshold(img, src, 200, 255, THRESH_TRUNC);
	threshold(src, src, 100, 255, THRESH_TOZERO);
	return src;
}

2,边缘检测

	Mat src = GetImage(i);
	Canny(src, src, 200, 100, 3);
	cv::imshow("src" + to_string(i), src);

3,轮廓检测

    std::vector<std::vector<Point>> contours;
	std::vector<Vec4i> hierarchy;
	findContours(src, contours, hierarchy, RETR_LIST, CHAIN_APPROX_NONE, Point(0, 0));
	cout << contours.size() << endl;
	sort(contours.begin(), contours.end(), cmp< Point>);
	for (int i = 0; i < contours.size(); i++)cout << contours[i].size() << " ";

把轮廓按照点数排序,点最多的轮廓就是我们需要的轮廓。

4,求解格子尺寸、坐标

int GetSize(const vector<std::vector<Point>>& contours, int &xmin ,int &ymin)
{
	xmin = 1234567, ymin = 1234567;
	int xmax = 0, ymax = 0;
	for (int i = 0; i < 5 && i < contours.size(); i++) {
		for (auto& pi : contours[i]) {
			if (xmin > pi.x)xmin = pi.x;
			if (ymin > pi.y)ymin = pi.y;
			if (xmax < pi.x)xmax = pi.x;
			if (ymax < pi.y)ymax = pi.y;
		}
	}
	int dx = xmax - xmin, dy = ymax - ymin;
	dx /= 7, dy /= 8;
	cout << endl << dx << endl << dy;
	return dx;
}

其中for循环控制的是取前多少个轮廓,否则很容易受到最外面的轮廓的影响。

xmin和ymin记录了左上角的格子坐标。

5,计算有效轮廓数量

为了更有效的计算格子尺寸,需要更智能的选择轮廓数量。

void GetContoursNum(Mat src, const vector<std::vector<Point>>& contours)
{
	bool xmin=false, xmax = false, ymin = false, ymax = false;
	contoursNum = 0;
	for (contoursNum = 0; contoursNum < contours.size(); contoursNum++) {
		for (auto &pi : contours[contoursNum]) {
			if (src.cols / 7 > pi.x)xmin = true;
			if (src.rows / 8 > pi.y)ymin = true;
			if (src.cols / 7*6 < pi.x)xmax = true;
			if (src.rows / 8*7 < pi.y)ymax = true;
		}
		if (xmin && ymin && xmax && ymax)break;
	}
	contoursNum++;
}

这对于一些场景有帮助,但是对于最外面的干扰轮廓很清晰的情况,还是无法避免。

6,坐标微调

计算的坐标还是容易受最外面的轮廓的影响,所以我们把坐标进行微调。

试了几个方法都不太好,先不做这个了,留一个接口,如果需要的话可以手动调。

void GetPos()
{
	xmin += 0, ymin += 0; // 手动调整
	return;
}

7,手动删减轮廓

最外面的轮廓干扰太大,所以最后我干脆提供一个手动删掉几个轮廓的接口。

void RemoveContours(vector<std::vector<Point>>& contours)
{
	int id[] = { 2,3 }; // 手动调整
	for (int i = sizeof(id) / sizeof(int); i >= 0; i--) {
		if (id[i] < contours.size())contours.erase(contours.begin() + id[i]);
	}
}

手动调坐标不太好操作,而且不精确,但根据显示的图很容易找出最外面的1-2个轮廓(如果有的话),这样就可以直接手动填写要删除的轮廓id重新运行。

8,解析空出来的3个格子

void GetInvalidPos(int size, Mat src)
{
	int x[8][7];
	int m, d, w, k = 0;
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++) {
		x[i][j] = 0;
		if (i < 2 && j == 6)continue;
		if (i == 7 && j < 4)continue;
		int r = ymin + size * i;
		int c = xmin + size * j;
		for (int row = r + size / 4; row < r + size / 4 * 3; row++) {
			for (int col = c + size / 4; col < c + size / 4 * 3; col++) {
				x[i][j] += int(src.at<uchar>(row, col));
			}
		}
		if (x[i][j] > 100) {
			if (k == 0) {
				m = i * 6 + j + 1; // 1-12
			} else if (k == 1) {
				d = (i - 2) * 7 + j + 1; // 1-31
			} else {
				w = (i - 6) * 3 + j - 3; // 0-6
			}
			k++;
		}
	}
	string s[] = { "日","一","二" ,"三" ,"四" ,"五" ,"六" };
	cout << m << "月" << d << "日周" << s[w];
}

8,连通性计算

void connect(Mat src)
{

	//for (int i = 0; i < src.rows; i++)for (int j = 0; j < src.cols; j++) {
	//	if (i%size_ == ymin % size_ || j % size_ == xmin % size_) {
	//		src.at<uchar>(i,j) = 200;
	//	}
	//}
	//imshow("src", src);

	bool up[8][7];
	for (int i = 1; i < 8; i++)for (int j = 0; j < 7; j++) {
		up[i][j] = true;
		if (!valid(i,j))continue;
		if (!valid(i-1, j))continue;
		int s = 0;
		//src.at<uchar>(ymin + size_ * i - size_ / 4, xmin + size_ * j + size_ / 4) = 200;
		//src.at<uchar>(ymin + size_ * i + size_ / 4, xmin + size_ * j + size_ / 4 * 3) = 200;
		for (int r = ymin + size_ * i - size_ / 4; r < ymin + size_ * i + size_ / 4; r++) {
			for (int c = xmin + size_ * j + size_ / 4; c < xmin + size_ * j + size_ / 4 * 3; c++) {
				if (int(src.at<uchar>(r, c)) > 10)s++;
			}
		}
		if (s > 5)up[i][j] = false;
	}
	//imshow("src", src);
	bool left[8][7];
	for (int i = 0; i < 8; i++)for (int j = 1; j < 7; j++) {
		left[i][j] = true;
		if (!valid(i, j))continue;
		if (!valid(i, j-1))continue;
		int s = 0;
		for (int r = ymin + size_ * i + size_ / 4; r < ymin + size_ * i + size_ / 4*3; r++) {
			for (int c = xmin + size_ * j - size_ / 4; c < xmin + size_ * j + size_ / 4; c++) {
				if (int(src.at<uchar>(r, c)) > 10)s++;
			}
		}
		if (s > 10)left[i][j] = false;
	}
	return;
}

计算每2个格子间的交接处有没有边缘检测出的点,判断2个格子是否连通。

再用并查集把连通的格子连起来。

bool valid2(int i, int j)
{
	if (!valid(i,j))return false;
	if (i == mi && j == mj)return false;
	if (i == di && j == dj)return false;
	if (i == wi && j == wj)return false;
	return true;
}
int fa[8 * 7]; // id=i*7+j
int find(int x)	//找祖先
{
	if (fa[x] == x)return x;
	return fa[x] = find(fa[x]);
}
void split()
{
	for (int i = 0; i < 56; i++)fa[i] = i;
	for (int i = 1; i < 8; i++)for (int j = 0; j < 7; j++) {
		if (!valid2(i, j))continue;
		if (!valid2(i - 1, j))continue;
		if (up[i][j])fa[find(i * 7 + j)] = find((i - 1) * 7 + j);
	}
	for (int i = 0; i < 8; i++)for (int j = 1; j < 7; j++) {
		if (!valid2(i, j))continue;
		if (!valid2(i, j-1))continue;
		if (left_[i][j])fa[find(i * 7 + j)] = find(i * 7 + j - 1);
	}
	map<int, int>m;
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++) {
		if (!valid2(i, j))continue;
		m[find(i * 7 + j)]++;
	}
	int k = 0;
	map<int, int>m2;
	for (auto &mi : m)m2[mi.first] = ++k;
	int block[8][7];
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 7; j++) {
			if (!valid2(i, j))block[i][j] = 0;
			else block[i][j] = m2[find(i * 7 + j)];
			cout <<setw(3)<< block[i][j];
		}
		cout << endl;
	}
	return;
}

9,完整代码



#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<map>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/core/mat.hpp>
using namespace std;
using namespace cv;


#pragma comment(lib,"../x64/vc14/lib/opencv_world452.lib")
#pragma comment(lib,"../x64/vc14/lib/opencv_world452d.lib")



int xmin, ymin, size_;
int contoursNum;
bool up[8][7];
bool left_[8][7];
int mi, mj, di, dj, wi, wj;

template<typename T>
bool cmp(vector<T> x, vector<T> y)
{
	return x.size() > y.size();
}

Mat GetImage(int i)
{
	Mat img = imread("D:/p/img (" + to_string(i) + ").jpg", 0);
	resize(img, img, Size(0, 0), 0.3, 0.3);
	Mat src;
	threshold(img, src, 200, 255, THRESH_TRUNC);
	threshold(src, src, 100, 255, THRESH_TOZERO);
	return src;
}
void RemoveContours(vector<std::vector<Point>>& contours)
{
	int id[] = { 100 }; // 手动调整
	for (int i = sizeof(id) / sizeof(int) -1; i >= 0; i--) {
		if (id[i] < contours.size())contours.erase(contours.begin() + id[i]);
	}
}
void GetContoursNum(Mat src, const vector<std::vector<Point>>& contours)
{
	bool xmin = false, xmax = false, ymin = false, ymax = false;
	contoursNum = 0;
	for (contoursNum = 0; contoursNum < contours.size(); contoursNum++) {
		for (auto& pi : contours[contoursNum]) {
			if (src.cols / 7 > pi.x)xmin = true;
			if (src.rows / 8 > pi.y)ymin = true;
			if (src.cols / 7 * 6 < pi.x)xmax = true;
			if (src.rows / 8 * 7 < pi.y)ymax = true;
		}
		if (xmin && ymin && xmax && ymax)break;
	}
	contoursNum++;
}
void GetSize(const vector<std::vector<Point>>& contours)
{
	xmin = 1234567, ymin = 1234567;
	int xmax = 0, ymax = 0;
	for (int i = 0; i < contoursNum && i < contours.size(); i++) {
		for (auto& pi : contours[i]) {
			if (xmin > pi.x)xmin = pi.x;
			if (ymin > pi.y)ymin = pi.y;
			if (xmax < pi.x)xmax = pi.x;
			if (ymax < pi.y)ymax = pi.y;
		}
	}
	int dx = xmax - xmin, dy = ymax - ymin;
	dx /= 7, dy /= 8;
	//cout << endl << dx << endl << dy;
	size_ = dx;
}

void GetPos()
{
	xmin += 0, ymin += 0; // 手动调整
	return;
}

bool valid(int i, int j)
{
	if (i < 2 && j == 6)return false;
	if (i == 7 && j < 4)return false;
	return true;
}

void GetInvalidPos(Mat src)
{
	int x[8][7];
	int m, d, w, k = 0;
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++) {
		x[i][j] = 0;
		if (!valid(i, j))continue;
		int r = ymin + size_ * i;
		int c = xmin + size_ * j;
		for (int row = r + size_ / 4; row < r + size_ / 4 * 3; row++) {
			for (int col = c + size_ / 4; col < c + size_ / 4 * 3; col++) {
				x[i][j] += int(src.at<uchar>(row, col));
			}
		}
		if (x[i][j] > 2000) {
			if (k == 0) {
				mi = i, mj = j;
				m = i * 6 + j + 1; // 1-12
			}
			else if (k == 1) {
				di = i, dj = j;
				d = (i - 2) * 7 + j + 1; // 1-31
			}
			else {
				wi = i, wj = j;
				w = (i - 6) * 3 + j - 3; // 0-6
			}
			k++;
		}
	}
	if (w < 0 || w>7) {
		cout << "error,w=" << w << endl;
		w = 0;
	}
	string s[] = { "日","一","二" ,"三" ,"四" ,"五" ,"六" };
	cout << m << "月" << d << "日周" << s[w] << endl;
}

void connect(Mat src)
{

	//for (int i = 0; i < src.rows; i++)for (int j = 0; j < src.cols; j++) {
	//	if (i%size_ == ymin % size_ || j % size_ == xmin % size_) {
	//		src.at<uchar>(i,j) = 200;
	//	}
	//}
	//imshow("src", src);


	for (int i = 1; i < 8; i++)for (int j = 0; j < 7; j++) {
		up[i][j] = true;
		if (!valid(i, j))continue;
		if (!valid(i - 1, j))continue;
		int s = 0;
		//src.at<uchar>(ymin + size_ * i - size_ / 4, xmin + size_ * j + size_ / 4) = 200;
		//src.at<uchar>(ymin + size_ * i + size_ / 4, xmin + size_ * j + size_ / 4 * 3) = 200;
		for (int r = ymin + size_ * i - size_ / 4; r < ymin + size_ * i + size_ / 4; r++) {
			for (int c = xmin + size_ * j + size_ / 4; c < xmin + size_ * j + size_ / 4 * 3; c++) {
				if (int(src.at<uchar>(r, c)) > 10)s++;
			}
		}
		if (s > 5)up[i][j] = false;
	}
	//imshow("src", src);

	for (int i = 0; i < 8; i++)for (int j = 1; j < 7; j++) {
		left_[i][j] = true;
		if (!valid(i, j))continue;
		if (!valid(i, j - 1))continue;
		int s = 0;
		for (int r = ymin + size_ * i + size_ / 4; r < ymin + size_ * i + size_ / 4 * 3; r++) {
			for (int c = xmin + size_ * j - size_ / 4; c < xmin + size_ * j + size_ / 4; c++) {
				if (int(src.at<uchar>(r, c)) > 10)s++;
			}
		}
		if (s > 10)left_[i][j] = false;
	}
	return;
}

bool valid2(int i, int j)
{
	if (!valid(i, j))return false;
	if (i == mi && j == mj)return false;
	if (i == di && j == dj)return false;
	if (i == wi && j == wj)return false;
	return true;
}
int fa[8 * 7]; // id=i*7+j
int find(int x)	//找祖先
{
	if (fa[x] == x)return x;
	return fa[x] = find(fa[x]);
}
void split()
{
	for (int i = 0; i < 56; i++)fa[i] = i;
	for (int i = 1; i < 8; i++)for (int j = 0; j < 7; j++) {
		if (!valid2(i, j))continue;
		if (!valid2(i - 1, j))continue;
		if (up[i][j])fa[find(i * 7 + j)] = find((i - 1) * 7 + j);
	}
	for (int i = 0; i < 8; i++)for (int j = 1; j < 7; j++) {
		if (!valid2(i, j))continue;
		if (!valid2(i, j - 1))continue;
		if (left_[i][j])fa[find(i * 7 + j)] = find(i * 7 + j - 1);
	}
	map<int, int>m;
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++) {
		if (!valid2(i, j))continue;
		m[find(i * 7 + j)]++;
	}
	using P = pair<int, int>;
	vector<P>v;
	for (auto& mi : m)v.push_back(mi);
	sort(v.begin(), v.end(), [](P p1, P p2) { return p1.second > p2.second; });
	int k = 0;
	map<int, int>m2;
	for (auto& vi : v)m2[vi.first] = ++k;
	int block[8][7];
	int pix = 50;
	Mat img = Mat(Size(pix * 7, pix * 8), CV_8UC1);
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 7; j++) {
			if (!valid2(i, j))block[i][j] = 0;
			else block[i][j] = m2[find(i * 7 + j)];
			cout << setw(3) << block[i][j];
			for (int r = pix * i; r < pix * (i + 1); r++) {
				for (int c = pix * j; c < pix * (j + 1); c++) {
					img.at<uchar>(r, c) = 25 * block[i][j];
				}
			}
			if (!valid2(i, j)) {
				for (int x = 0; x < pix; x++) {
					img.at<uchar>(pix * i + x, pix * j + x) = 255;
					img.at<uchar>(pix * i + x, pix * j + pix - x -1) = 255;
				}
			}
		}
		cout << endl;
	}
	static int kid = 0;
	imshow("ans"+to_string(kid++),img);
	return;
}

void f(int i)
{
	Mat src = GetImage(i);
	Canny(src, src, 200, 100, 3);
	//cv::imshow("src" + to_string(i), src);

	std::vector<std::vector<Point>> contours;
	std::vector<Vec4i> hierarchy;
	findContours(src, contours, hierarchy, RETR_LIST, CHAIN_APPROX_NONE, Point(0, 0));
	//cout << contours.size() << endl;
	sort(contours.begin(), contours.end(), cmp< Point>);
	RemoveContours(contours);
	//for (int i = 0; i < contours.size(); i++)cout << contours[i].size() << " ";
	GetContoursNum(src, contours);

	Mat img(Size(src.cols, src.rows), src.type());
	img = 0;
	for (int i = 0; i < contoursNum && i < contours.size(); i++) {
		cv::drawContours(img, contours, i, cv::Scalar::all(255));
		//cv::imshow("contours" + to_string(i), img);
	}

	GetSize(contours);
	GetPos();
	GetInvalidPos(src);
	connect(src);
	split();
}

int main()
{
	for (int i = 7; i <= 11; i++)
	{
		f(i);
	}
	cv::waitKey(0);
	return 0;
}

运行效果:

可以看到数字化完全正确,为了方便校验做出来的灰度图也一致。

在10张照片里面有一张运行有点问题:

有2个块连起来了,这种情况只能手动微改一下了。

六,以解生解

每完成一个月的解法,就可以按照大拇指和U型分别生成解,把找到的新解存下来以作备用。

1,大拇指

把形似大拇指的这个块,通过翻转,可以生成不同的解。

(1)识别大拇指 

//#include "data.h"

#include<iostream>
#include <vector>
using namespace std;

#define OUT(x) cout << endl << #x << " = "; Print(x);
template<typename T>
inline void Print(T x)
{
	cout << x << " ";
}
template<typename T>
inline void Read(T& x)
{
	while (!(cin >> x)) { // only cin type T, ignore other info
		cin.clear();
		cin.ignore();
	}
}

const float theNan = 0.123456; //float默认只有6位
void Print(float x)
{
	if (std::isnan(x)) cout << theNan << " ";
	else cout << x << " ";
}
void Read(float& x)
{
	Read<float>(x);
	if (x == theNan) x = NAN;
}

int main()
{
	int x[8][7];
	while (true)
	{
		for (int i = 0; i < 8; i++)
		{
			for (int j = 0; j < 7; j++)
			{
				Read(x[i][j]);
			}
		}
		for (int k = 1; k < 10; k++)
		{
			int xmin = 10, ymin = 10, xmax = 0, ymax = 0;
			for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)
			{
				if (x[i][j] != k)continue;
				xmin = min(xmin, i);
				ymin = min(ymin, j);
				xmax = max(xmax, i);
				ymax = max(ymax, j);
			}
			if ((xmax - xmin + 1) * (ymax - ymin + 1) != 6)continue;
			bool flag = true;
			int n = 0;
			for (int i = xmin; i <= xmax && flag; i++)for (int j = ymin; j <= ymax && flag; j++)
			{
				if (x[i][j] == k)continue;
				if (x[i][j])flag = false;
				n++;
			}
			if (!flag || n > 1)continue;
			if (x[xmin][ymin] && x[xmin][ymax] && x[xmax][ymin] && x[xmax][ymax])continue;
			cout << "                                  "<<k << endl;
		}
	}
	return 0;
}

(2)进行翻转

除了当前解,还可以生成3个不同的解

            for (int i = 0; i < 3; i++) {
				int tmp = x[xmin][ymin];
				x[xmin][ymin] = x[xmin][ymax];
				x[xmin][ymax] = x[xmax][ymin];
				x[xmax][ymin] = x[xmax][ymax];
				x[xmax][ymax] = tmp;

			}

(3)校验是否合法

int x[8][7];
int m, d, w;
string s[] = { "日","一","二" ,"三" ,"四" ,"五" ,"六" };
bool valid(int i, int j)
{
	if (i < 2 && j == 6)return false;
	if (i == 7 && j < 4)return false;
	return true;
}
bool check()
{
	int k = 0;
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++) {
		if (!valid(i, j))continue;
		if (x[i][j] == 0) {
			if (k == 0) {
				if (i > 1)
					return false;
				m = i * 6 + j + 1; // 1-12
			}
			else if (k == 1) {
				if (i <= 1)
					return false;
				if (i * 7 + j >= 45)
					return false;
				d = (i - 2) * 7 + j + 1; // 1-31
			}
			else {
				if (i * 7 + j < 45)
					return false;
				w = (i - 6) * 3 + j - 3; // 0-6
			}
			k++;
		}
	}
}

2,可视化

为了方便查看,单独把显示图像的函数提取出来。

int x[8][7];

void show()
{
	int pix = 50;
	Mat img = Mat(Size(pix * 7, pix * 8), CV_8UC1);
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 7; j++) {
			for (int r = pix * i; r < pix * (i + 1); r++) {
				for (int c = pix * j; c < pix * (j + 1); c++) {
					img.at<uchar>(r, c) = 25 * x[i][j];
				}
			}
			if (x[i][j]==0) {
				for (int x = 0; x < pix; x++) {
					img.at<uchar>(pix * i + x, pix * j + x) = 255;
					img.at<uchar>(pix * i + x, pix * j + pix - x - 1) = 255;
				}
			}
		}
		cout << endl;
	}
	static int kid = 0;
	imshow("ans" + to_string(kid++), img);
}

int main()
{
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)Read(x[i][j]);
	show();
	cv::waitKey(0);
	return 0;
}

3,U型

和大拇指类似,U型也可以用来生成解。

代码几乎是一样的,微改一下即可:

int main()
{
	freopen("D:/out.txt", "w", stdout);
	while (true)
	{
		for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)Read(x[i][j]);
		for (int k = 1; k < 10; k++)
		{
			int xmin = 10, ymin = 10, xmax = 0, ymax = 0;
			for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)
			{
				if (x[i][j] != k)continue;
				xmin = min(xmin, i);
				ymin = min(ymin, j);
				xmax = max(xmax, i);
				ymax = max(ymax, j);
			}
			if ((xmax - xmin + 1) * (ymax - ymin + 1) != 6)continue;
			bool flag = true;
			int n = 0;
			for (int i = xmin; i <= xmax && flag; i++)for (int j = ymin; j <= ymax && flag; j++)
			{
				if (x[i][j] == k)continue;
				if (x[i][j])flag = false;
				n++;
			}
			if (!flag || n > 1)continue;
			if (!(x[xmin][ymin] && x[xmin][ymax] && x[xmax][ymin] && x[xmax][ymax]))continue;
			//cout << "                                  "<<k << endl;
			int tmp = x[(xmin + xmax) / 2][(ymin + ymax) / 2];
			x[(xmin + xmax) / 2][(ymin + ymax) / 2] = x[xmin + 1][ymin + 1], x[xmin + 1][ymin + 1] = tmp;
			if (check()) {
				cout << m << "月" << d << "日周" << s[w] << endl;
				for (int i = 0; i < 8; i++) {
					for (int j = 0; j < 7; j++) {
						cout << setw(3) << x[i][j];
					}
					cout << endl;
				}
			}
			break;
		}
	}
	return 0;
}

4,新解

由于产生的新解太多,所以挪到本地磁盘了。

5,日期汇总

为了自动去重,建立日期汇总,每次有一个解产生时,都把日期加进来。以本地文件为准。

6,完整代码V1

//#include "data.h"

#include <iostream>
#include <vector>
#include <iomanip>
#include <map>
#include <string>
using namespace std;

#define OUT(x) cout << endl << #x << " = "; Print(x);
template<typename T>
inline void Print(T x)
{
	cout << x << " ";
}
template<typename T>
inline void Read(T& x)
{
	while (!(cin >> x)) { // only cin type T, ignore other info
		cin.clear();
		cin.ignore();
	}
}

const float theNan = 0.123456; //float默认只有6位
void Print(float x)
{
	if (std::isnan(x)) cout << theNan << " ";
	else cout << x << " ";
}
void Read(float& x)
{
	Read<float>(x);
	if (x == theNan) x = NAN;
}

int x[8][7];
int m, d, w;
string s[] = { "日","一","二" ,"三" ,"四" ,"五" ,"六" };
bool valid(int i, int j)
{
	if (i < 2 && j == 6)return false;
	if (i == 7 && j < 4)return false;
	return true;
}
bool check()
{
	int k = 0;
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++) {
		if (!valid(i, j))continue;
		if (x[i][j] == 0) {
			if (k == 0) {
				if (i > 1)
					return false;
				m = i * 6 + j + 1; // 1-12
			}
			else if (k == 1) {
				if (i <= 1)
					return false;
				if (i * 7 + j >= 45)
					return false;
				d = (i - 2) * 7 + j + 1; // 1-31
			}
			else {
				if (i * 7 + j < 45)
					return false;
				w = (i - 6) * 3 + j - 3; // 0-6
			}
			k++;
		}
	}
}

int main()
{
	freopen("D:/p/date.txt", "r", stdin);
	string s1;
	map<string, int>sm;
	while (cin >> s1)sm[s1] = 1;
	freopen("CON", "r", stdin);
	while (true)
	{
		for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)Read(x[i][j]);
		for (int k = 1; k < 10; k++)
		{
			int xmin = 10, ymin = 10, xmax = 0, ymax = 0;
			for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)
			{
				if (x[i][j] != k)continue;
				xmin = min(xmin, i);
				ymin = min(ymin, j);
				xmax = max(xmax, i);
				ymax = max(ymax, j);
			}
			if ((xmax - xmin + 1) * (ymax - ymin + 1) != 6)continue;
			bool flag = true;
			int n = 0;
			for (int i = xmin; i <= xmax && flag; i++)for (int j = ymin; j <= ymax && flag; j++)
			{
				if (x[i][j] == k)continue;
				if (x[i][j])flag = false;
				n++;
			}
			if (!flag || n > 1)continue;
			if (!(x[xmin][ymin] && x[xmin][ymax] && x[xmax][ymin] && x[xmax][ymax])) {
				for (int i = 0; i < 4; i++) {
					int tmp = x[xmin][ymin];
					x[xmin][ymin] = x[xmin][ymax];
					x[xmin][ymax] = x[xmax][ymin];
					x[xmax][ymin] = x[xmax][ymax];
					x[xmax][ymax] = tmp;
					
					if (check()) {
						string ts = to_string(m) + "月" + to_string(d) + "日周" + s[w];
						if (sm[ts])continue;
						sm[ts] = 1;
						cout << ts << endl;
						for (int i = 0; i < 8; i++) {
							for (int j = 0; j < 7; j++) {
								cout << setw(3) << x[i][j];
							}
							cout << endl;
						}
					}
				}
			}
			else {
				int tmp = x[(xmin + xmax) / 2][(ymin + ymax) / 2];
				x[(xmin + xmax) / 2][(ymin + ymax) / 2] = x[xmin + 1][ymin + 1], x[xmin + 1][ymin + 1] = tmp;
				if (check()) {
					string ts = to_string(m) + "月" + to_string(d) + "日周" + s[w];
					if (sm[ts])continue;
					sm[ts] = 1;
					cout << ts << endl;
					for (int i = 0; i < 8; i++) {
						for (int j = 0; j < 7; j++) {
							cout << setw(3) << x[i][j];
						}
						cout << endl;
					}
				}
			}
		}
	}
	return 0;
}

7,BUG修复

如果同时存在大拇指和U型新解,那么上面的代码只能产生一部分新解。

由于这个代码已经运行一个月,目前一共已经得到400种不同的解法,所以解决方法最好还要能修复之前缺失的新解。

所以我把控制台输入改成从文件输入,把历史所有解当做输入,清算有没有漏的。

再修复一个BUG,之前的代码遗漏了数字为10的块也可以以解生解。

代码略,参见下一节。

8,日期优化(完整代码V2)

记录日期的文件除了被程序读取使用外,还被我肉眼读,因为我经常手动以解生解,需要查看哪些日期还没有解。

日期逐渐多了之后,不太好看了,所以我优化了数据结构,以“-1月1日”表示1月1日的从周一到周日的七个组合都有解了。

代码只需要把读取日期记录文件的地方略改即可。

完整代码:

#include "data.h"

#include <iostream>
#include <vector>
#include <iomanip>
#include <map>
#include <string>
using namespace std;

#define OUT(x) cout << endl << #x << " = "; Print(x);
template<typename T>
inline void Print(T x)
{
	cout << x << " ";
}
template<typename T>
inline void Read(T& x)
{
	while (!(cin >> x)) { // only cin type T, ignore other info
		cin.clear();
		cin.ignore();
	}
}

const float theNan = 0.123456; //float默认只有6位
void Print(float x)
{
	if (std::isnan(x)) cout << theNan << " ";
	else cout << x << " ";
}
void Read(float& x)
{
	Read<float>(x);
	if (x == theNan) x = NAN;
}

int x[8][7];
int m, d, w;
string s[] = { "日","一","二" ,"三" ,"四" ,"五" ,"六" };
bool valid(int i, int j)
{
	if (i < 2 && j == 6)return false;
	if (i == 7 && j < 4)return false;
	return true;
}
bool check()
{
	int k = 0;
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++) {
		if (!valid(i, j))continue;
		if (x[i][j] == 0) {
			if (k == 0) {
				if (i > 1)
					return false;
				m = i * 6 + j + 1; // 1-12
			}
			else if (k == 1) {
				if (i <= 1)
					return false;
				if (i * 7 + j >= 45)
					return false;
				d = (i - 2) * 7 + j + 1; // 1-31
			}
			else {
				if (i * 7 + j < 45)
					return false;
				w = (i - 6) * 3 + j - 3; // 0-6
			}
			k++;
		}
	}
	if (m == 2 && d > 29)return false;
	if ((m == 4 || m == 6 || m == 9 || m == 11) && d > 30)return false;
	return true;
}

void go()
{
	freopen("D:/p/date.txt", "r", stdin);
	string s1;
	map<string, int>sm;
	while (cin >> s1) {
		sm[s1] = 1;
		if (s1[0] == '-') {
			s1 = s1.substr(1, s1.length() - 1);
			for (int i = 0; i < 7; i++)sm[s1 + "周" + s[i]] = 1;
		}
	}
	freopen("D:/p/ans.txt", "r", stdin);
	while (true)
	{
		int a, b;
		Read(a); Read(b);
		for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)Read(x[i][j]);
		for (int k = 1; k <= 10; k++)
		{
			int xmin = 10, ymin = 10, xmax = 0, ymax = 0;
			for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)
			{
				if (x[i][j] != k)continue;
				xmin = min(xmin, i);
				ymin = min(ymin, j);
				xmax = max(xmax, i);
				ymax = max(ymax, j);
			}
			if ((xmax - xmin + 1) * (ymax - ymin + 1) != 6)continue;
			bool flag = true;
			int n = 0;
			for (int i = xmin; i <= xmax && flag; i++)for (int j = ymin; j <= ymax && flag; j++)
			{
				if (x[i][j] == k)continue;
				if (x[i][j])flag = false;
				n++;
			}
			if (!flag || n > 1)continue;
			if (!(x[xmin][ymin] && x[xmin][ymax] && x[xmax][ymin] && x[xmax][ymax])) {
				for (int i = 0; i < 4; i++) {
					int tmp = x[xmin][ymin];
					x[xmin][ymin] = x[xmin][ymax];
					x[xmin][ymax] = x[xmax][ymin];
					x[xmax][ymin] = x[xmax][ymax];
					x[xmax][ymax] = tmp;
					if (check()) {
						string ts = to_string(m) + "月" + to_string(d) + "日周" + s[w];
						if (sm[ts])continue;
						sm[ts] = 1;
						cout << ts << endl;
						for (int i = 0; i < 8; i++) {
							for (int j = 0; j < 7; j++) {
								cout << setw(3) << x[i][j];
							}
							cout << endl;
						}
					}
				}
			}
			else {
				int tmp = x[(xmin + xmax) / 2][(ymin + ymax) / 2];
				x[(xmin + xmax) / 2][(ymin + ymax) / 2] = x[xmin + 1][ymin + 1], x[xmin + 1][ymin + 1] = tmp;
				if (check()) {
					string ts = to_string(m) + "月" + to_string(d) + "日周" + s[w];
					if (sm[ts])continue;
					sm[ts] = 1;
					cout << ts << endl;
					for (int i = 0; i < 8; i++) {
						for (int j = 0; j < 7; j++) {
							cout << setw(3) << x[i][j];
						}
						cout << endl;
					}
				}
			}
		}
	}
}

int main()
{
	go();
	return 0;
}

9,BUG修复、新增检查(完整代码V3)

经过了前面的BUG修复之后,仍然存在以解生解不全的问题。

所以我又把2种以解生解的模式做了拆分,先做U型再做大拇指,分开成2次编译运行,先只调用go后只调用go1,这样才能确保生成所有的解。

然后新增了go2和go3,需要检查的时候调用即可。

完整代码:

#include<iostream>
using namespace std;

#define OUT(x) cout << endl << #x << " = "; Print(x);
template<typename T>
inline void Print(T x)
{
	cout << x << " ";
}
template<typename T>
inline void Read(T& x)
{
	while (!(cin >> x)) { // only cin type T, ignore other info
		cin.clear();
		cin.ignore();
	}
}

const float theNan = 0.123456; //float默认只有6位
void Print(float x)
{
	if (std::isnan(x)) cout << theNan << " ";
	else cout << x << " ";
}
void Read(float& x)
{
	Read<float>(x);
	if (x == theNan) x = NAN;
}
template<typename T1, typename T2>
inline void Print(const std::pair<T1, T2>& p)
{
	Print(p.first);
	Print(p.second);
}
template<typename T1, typename T2>
inline void Print(const map<T1, T2>& aMap)
{
	cout << " size = " << aMap.size() << endl;
	for (auto& it : aMap) {
		Print(it);
		cout << endl;
	}
}
template<typename T>
inline void Print(const vector<T>& vec)
{
	cout << " size = " << vec.size() << endl;
	for (auto& it : vec) {
		Print(it);
	}
}
template<typename T1, typename T2>
inline void Read(std::pair<T1, T2>& p)
{
	Read(p.first);
	Read(p.second);
}
template<typename Tkey, typename Tvalue>
void Read(std::map<Tkey, Tvalue>& aMap)
{
	int num;
	Read(num);
	std::pair<Tkey, Tvalue> p;
	while (num--) {
		Read(p);
		aMap[p.first] = p.second;
	}
}
template<typename T>
inline void Read(vector<T>& vec)
{
	int num;
	Read(num);
	vec.resize(num); // 慎用
	for (int i = 0; i < num; i++) {
		Read(vec[i]);
	}
}

int x[8][7];
int m, d, w;
string s[] = { "日","一","二" ,"三" ,"四" ,"五" ,"六" };
bool valid(int i, int j)
{
	if (i < 2 && j == 6)return false;
	if (i == 7 && j < 4)return false;
	return true;
}
bool check()
{
	int k = 0;
	for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++) {
		if (!valid(i, j))continue;
		if (x[i][j] == 0) {
			if (k == 0) {
				if (i > 1)
					return false;
				m = i * 6 + j + 1; // 1-12
			}
			else if (k == 1) {
				if (i <= 1)
					return false;
				if (i * 7 + j >= 45)
					return false;
				d = (i - 2) * 7 + j + 1; // 1-31
			}
			else {
				if (i * 7 + j < 45)
					return false;
				w = (i - 6) * 3 + j - 3; // 0-6
			}
			k++;
		}
	}
	if (m == 2 && d > 29)return false;
	if ((m == 4 || m == 6 || m == 9 || m == 11) && d > 30)return false;
	return true;
}

void go() //以解生解
{
	freopen("D:/p/date.txt", "r", stdin);
	string s1;
	map<string, int>sm;
	while (cin >> s1) {
		sm[s1] = 1;
		if (s1[0] == '-') {
			s1 = s1.substr(1, s1.length() - 1);
			for (int i = 0; i < 7; i++)sm[s1 + "周" + s[i]] = 1;
		}
	}
	freopen("D:/p/ans.txt", "r", stdin);
	while (true)
	{
		int a, b;
		Read(a); Read(b);
		for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)Read(x[i][j]);
		for (int k = 1; k <= 10; k++)
		{
			int xmin = 10, ymin = 10, xmax = 0, ymax = 0;
			for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)
			{
				if (x[i][j] != k)continue;
				xmin = min(xmin, i);
				ymin = min(ymin, j);
				xmax = max(xmax, i);
				ymax = max(ymax, j);
			}
			if ((xmax - xmin + 1) * (ymax - ymin + 1) != 6)continue;
			bool flag = true;
			int n = 0;
			for (int i = xmin; i <= xmax && flag; i++)for (int j = ymin; j <= ymax && flag; j++)
			{
				if (x[i][j] == k)continue;
				if (x[i][j])flag = false;
				n++;
			}
			if (!flag || n > 1)continue;
			if (x[xmin][ymin] && x[xmin][ymax] && x[xmax][ymin] && x[xmax][ymax]) // U型
			{ 
				int tmp = x[(xmin + xmax) / 2][(ymin + ymax) / 2];
				x[(xmin + xmax) / 2][(ymin + ymax) / 2] = x[xmin + 1][ymin + 1], x[xmin + 1][ymin + 1] = tmp;
				if (check()) {
					string ts = to_string(m) + "月" + to_string(d) + "日周" + s[w];
					if (sm[ts])continue;
					sm[ts] = 1;
					cout << ts << endl;
					for (int i = 0; i < 8; i++) {
						for (int j = 0; j < 7; j++) {
							cout << setw(3) << x[i][j];
						}
						cout << endl;
					}
				}
			}
		}
	}
}
void go1() //以解生解
{
	freopen("D:/p/date.txt", "r", stdin);
	string s1;
	map<string, int>sm;
	while (cin >> s1) {
		sm[s1] = 1;
		if (s1[0] == '-') {
			s1 = s1.substr(1, s1.length() - 1);
			for (int i = 0; i < 7; i++)sm[s1 + "周" + s[i]] = 1;
		}
	}
	freopen("D:/p/ans.txt", "r", stdin);
	while (true)
	{
		int a, b;
		Read(a); Read(b);
		for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)Read(x[i][j]);
		for (int k = 1; k <= 10; k++)
		{
			int xmin = 10, ymin = 10, xmax = 0, ymax = 0;
			for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)
			{
				if (x[i][j] != k)continue;
				xmin = min(xmin, i);
				ymin = min(ymin, j);
				xmax = max(xmax, i);
				ymax = max(ymax, j);
			}
			if ((xmax - xmin + 1) * (ymax - ymin + 1) != 6)continue;
			bool flag = true;
			int n = 0;
			for (int i = xmin; i <= xmax && flag; i++)for (int j = ymin; j <= ymax && flag; j++)
			{
				if (x[i][j] == k)continue;
				if (x[i][j])flag = false;
				n++;
			}
			if (!flag || n > 1)continue;
			if (!(x[xmin][ymin] && x[xmin][ymax] && x[xmax][ymin] && x[xmax][ymax])) //大拇指
			{
				for (int i = 0; i < 4; i++) {
					int tmp = x[xmin][ymin];
					x[xmin][ymin] = x[xmin][ymax];
					x[xmin][ymax] = x[xmax][ymin];
					x[xmax][ymin] = x[xmax][ymax];
					x[xmax][ymax] = tmp;
					if (check()) {
						string ts = to_string(m) + "月" + to_string(d) + "日周" + s[w];
						if (sm[ts])continue;
						sm[ts] = 1;
						cout << ts << endl;
						for (int i = 0; i < 8; i++) {
							for (int j = 0; j < 7; j++) {
								cout << setw(3) << x[i][j];
							}
							cout << endl;
						}
					}
				}
			}
		}
	}
}

void go2()//检查日期记录有没有漏的
{
	string s1;
	map<string, int>sm;
	freopen("D:/p/date.txt", "r", stdin);
	while (cin >> s1) {
		sm[s1] = 1;
		if (s1[0] == '-') {
			s1 = s1.substr(1, s1.length() - 1);
			for (int i = 0; i < 7; i++)sm[s1 + "周" + s[i]] = 1;
		}
	}
	cin.clear();
	freopen("CON", "r", stdin);
	freopen("D/p/out.txt", "w", stdout);
	while (cin >> s1)
	{
		for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)Read(x[i][j]);
		if (sm[s1] == 0)cout << s1 << endl;
	}
}
void go3()//检查有没有重复日期的解
{
	string s1;
	map<string, int>sm;
	freopen("D/p/out.txt", "w", stdout);
	while (cin >> s1)
	{
		for (int i = 0; i < 8; i++)for (int j = 0; j < 7; j++)Read(x[i][j]);
		if (sm[s1] == 1)cout << s1 << endl;
		sm[s1] = 1;
	}
}



int main()
{
    //go();
	go1();
	return 0;
}

七,说明

1,每天可以在本文搜索一下,如果已经有解了就不用再拼了。否则,新拼出一个解时,把照片放到本地,等每隔一段时间再统一数字化。

2,数字化时,通过批量命名把所有图片改成img*.jpg,然后运行第五章第9节的完整代码,把图片转化成数字。

3,把数字化的结果更新到(本地)结果汇总中,把其中的日期,更新到(本地)日期汇总中。

4,运行第六章第7节的完整代码V2,产生新解,跳到第3步,循环,直到再也没有新解。

5,如果想查看一个数字化的解,可以运行上面第六章第2节的代码,也可以运行下面的可视化V2版。

八,直接求解

1,可视化V2版

对显示答案做了微调,把月日周对应的3个格子用真实的内容显示出来。

int x[8][7];

void show()
{
	int pix = 50;
	Mat img = imread("D:/base.jpg",0);
	resize(img, img, Size(pix * 7, pix * 8), 0, 0);
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 7; j++) {
			if(x[i][j])for (int r = pix * i; r < pix * (i + 1); r++) {
				for (int c = pix * j; c < pix * (j + 1); c++) {
					img.at<uchar>(r, c) = 25 * x[i][j];
				}
			}
		}
		cout << endl;
	}
	static int kid = 0;
	imshow("ans" + to_string(kid++), img);
}

其中D:/base.jpg就是没有块的图:

运行效果:

2,直接求解

利用拼接覆盖问题通用求解代码,直接把12*31*7的所有组合全部求出来。

int r,c,blockNum; //自定义行列数,块数
map<Grid, int> ng,mg;  //ng是自定义挖掉的格子,mg是有效格子
vector<Block>blocks;//自定义每个块的所有形态在最小位置包含的格子

vector<Grid> rotate(vector<Grid>& g)
{
	int maxRow = 0, t;
	for (auto& gi : g)maxRow = max(maxRow, gi.r);
	for (auto& gi : g)t = gi.c, gi.c = maxRow - gi.r, gi.r = t;
	return g;
}
vector<Grid> reverse(vector<Grid> &g)
{
	for (auto& gi : g)gi.r ^= gi.c ^= gi.r ^= gi.c;
	return g;
}

void init1(int m,int d,int w)
{
	r = 8, c = 7, blockNum = 10;
	ng.clear(), mg.clear();
	ng[Grid{ 0,6 }] = ng[Grid{ 1,6 }] = ng[Grid{ 7,0 }] = ng[Grid{ 7,1 }] = ng[Grid{ 7,2 }] = ng[Grid{ 7,3 }] = 1;
	m--, d--, w %= 7;
	ng[Grid{ m / 6,m % 6 }] = ng[Grid{ d / 7 + 2,d % 7 }] = 1;
	ng[Grid{ w / 4 + 6,w % 4 + 3 + (w > 3) }] = 1;
}
void init2()
{
	vector<Grid>v = { {0,0},{0,1},{1,1},{1,2},{1,3} };
	blocks[0] = Block{ { v,rotate(v),rotate(v),rotate(v), reverse(v),rotate(v),rotate(v),rotate(v)}, r, c, mg };
	v = { {0,0},{0,1},{0,2},{1,0},{1,1} };
	blocks[1] = Block{ { v,rotate(v),rotate(v),rotate(v), reverse(v),rotate(v),rotate(v),rotate(v)}, r, c, mg };
	v = { {0,0},{0,1},{0,2},{1,0} };
	blocks[2] = Block{ { v,rotate(v),rotate(v),rotate(v), reverse(v),rotate(v),rotate(v),rotate(v)}, r, c, mg };
	v = { {0,0},{0,1},{0,2},{0,3}, {1,0} };
	blocks[3] = Block{ { v,rotate(v),rotate(v),rotate(v), reverse(v),rotate(v),rotate(v),rotate(v)}, r, c, mg };
	v = { {0,0},{0,1},{0,2},{1,0},{2,0} };
	blocks[4] = Block{ {v,rotate(v),rotate(v),rotate(v)},r,c, mg };
	v = { {0,0},{0,1},{0,2},{1,1},{2,1} };
	blocks[5] = Block{ {v,rotate(v),rotate(v),rotate(v)},r,c, mg };
	v = { {0,0},{0,1},{0,2},{0,3} };
	blocks[6] = Block{ {v,rotate(v)},r,c, mg };
	v = { {0,0},{0,1},{1,1},{1,2} };
	blocks[7] = Block{ { v,rotate(v), reverse(v),rotate(v)}, r, c, mg };
	v = { {0,0},{0,1},{0,2},{1,0} ,{1,2} };
	blocks[8] = Block{ {v,rotate(v),rotate(v),rotate(v)},r,c, mg };
	v = { {0,0},{0,1},{1,1},{2,1},{2,2} };
	blocks[9] = Block{ {v,rotate(v),reverse(v),rotate(v)},r,c, mg };
}

void solve(int m, int d, int w)
{
	init1(m, d, w);
	int id = 0;
	for (int i = 0; i < r; i++)for (int j = 0; j < c; j++) {
		if (ng[Grid{ i, j }] == 0)mg[Grid{ i, j }] = ++id;
	}
	blocks.resize(blockNum);
	init2();
	vector<vector<Grid>> grids = Cover(blocks, mg);
	vector<vector<int>>v(r);
	for (int i = 0; i < r; i++)v[i].resize(c);
	for (int i = 0; i < grids.size(); i++) {
		for (auto& g : grids[i])v[g.r][g.c] = i + 1;
	}
	for (int i = 0; i < r; i++) {
		for (int j = 0; j < v[i].size(); j++)cout << v[i][j] << " ";
		cout << endl;
	}
}

int main()
{
	ios::sync_with_stdio(false);
	clock_t start, endd;
	start = clock();
	freopen("D:ans.txt", "w", stdout);
	for (int m = 1; m <= 12; m++)for (int d = 1; d <= 31; d++)for (int w = 1; w <= 7; w++) {
		solve(m, d, w);
		cout << endl;
	}
	endd = clock();
	double endtime = (double)(endd - start) / CLOCKS_PER_SEC;
	cout << "Total time:" << endtime << endl; //s为单位
	return 0;
}

输出:

0 3 3 3 9 9 0 
2 2 3 8 9 9 0 
0 2 3 8 8 9 10 
5 2 2 7 8 10 10 
5 7 7 7 8 10 4 
5 5 5 6 1 1 4 
6 6 6 6 0 1 4 
0 0 0 0 1 1 4 

0 3 3 4 4 4 0 
2 2 3 3 4 6 0 
0 2 5 5 4 6 7 
8 2 2 5 10 6 7 
8 8 9 5 10 6 7 
8 8 9 10 10 7 7 
9 9 9 10 1 0 1 
0 0 0 0 1 1 1 

0 5 5 5 6 6 0 
4 4 5 9 10 6 0 
0 4 5 9 10 6 3 
4 4 8 9 10 10 3 
7 8 8 9 9 10 3 
7 8 8 2 1 1 3 
7 7 7 2 2 1 0 
0 0 0 0 2 1 1 

0 2 2 3 3 3 0 
1 1 2 2 3 8 0 
0 1 5 5 3 8 8 
4 1 1 5 10 8 8 
4 6 5 5 10 10 7 
4 6 9 9 9 10 7 
4 6 6 6 9 10 7 
0 0 0 0 0 7 7 

0 3 3 4 4 4 0 
2 2 3 3 4 6 0 
0 2 5 5 4 6 8 
7 2 2 5 10 6 8 
7 7 9 5 10 6 8 
7 7 9 10 10 8 8 
9 9 9 10 1 1 1 
0 0 0 0 1 0 1 

0 2 2 3 3 3 0 
1 1 2 2 3 8 0 
0 1 5 5 3 8 10 
4 1 1 5 8 8 10 
4 6 5 5 9 9 10 
4 6 9 9 9 10 10 
4 6 6 6 7 7 7 
0 0 0 0 7 7 0 

0 2 2 3 3 3 0 
1 1 2 2 3 9 0 
0 1 6 6 3 9 9 
4 1 1 6 7 9 9 
4 5 5 6 7 10 8 
4 5 7 7 7 10 8 
4 5 5 0 10 10 8 
0 0 0 0 10 8 8 

0 2 2 10 10 10 0 
2 2 8 8 8 10 0 
3 0 8 9 9 10 7 
3 9 9 9 7 7 7 
3 5 6 6 7 4 4 
3 5 6 6 1 1 4 
5 5 5 6 0 1 4 
0 0 0 0 1 1 4 

0 2 2 5 5 5 0 
2 2 5 5 10 7 0 
3 0 10 10 10 7 9 
3 3 3 8 10 7 9 
4 4 3 8 7 7 9 
4 6 6 8 8 8 9 
4 6 6 6 1 0 1 
0 0 0 0 1 1 1 

0 4 4 4 6 6 0 
5 5 4 9 10 6 0 
5 0 4 9 10 6 3 
5 5 8 9 10 10 3 
7 8 8 9 9 10 3 
7 8 8 2 1 1 3 
7 7 7 2 2 1 0 
0 0 0 0 2 1 1 

0 3 3 4 4 6 0 
2 2 3 3 4 6 0 
2 0 9 8 4 6 6 
2 2 9 8 8 8 6 
10 10 9 8 5 5 5 
10 10 9 9 1 1 5 
10 7 7 7 7 1 5 
0 0 0 0 0 1 1 

0 2 2 5 5 5 0 
2 2 5 5 10 7 0 
3 0 10 10 10 7 9 
3 3 3 8 10 7 9 
4 4 3 8 7 7 9 
4 6 6 8 8 8 9 
4 6 6 6 1 1 1 
0 0 0 0 1 0 1 

0 3 3 3 3 8 0 
1 1 8 8 8 8 0 
1 0 10 10 9 9 9 
1 1 4 10 10 10 9 
4 4 4 6 6 6 9 
4 2 2 6 5 7 7 
2 2 5 5 5 7 7 
0 0 0 0 5 7 0 

0 3 3 5 5 5 0 
1 1 3 5 9 10 0 
1 0 3 3 9 10 10 
1 1 4 6 9 10 10 
2 4 4 6 9 7 8 
2 4 6 6 6 7 8 
2 2 2 0 7 7 8 
0 0 0 0 7 8 8 

......省略2千多个解

4 2 2 2 1 1 0 
4 2 5 2 1 0 0 
4 4 5 1 1 6 3 
9 4 5 5 5 6 3 
9 9 9 8 8 6 3 
9 10 10 8 6 6 3 
10 10 0 8 7 7 7 
0 0 0 0 7 7 0 

4 2 2 2 1 1 0 
4 2 7 2 1 0 0 
4 7 7 1 1 5 3 
4 7 5 5 5 5 3 
10 7 9 9 3 3 3 
10 9 9 8 8 8 6 
10 10 0 0 8 6 6 
0 0 0 0 8 6 6 

Total time:12.741
 

  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
vue-puzzle是一个Vue.js的人机验证组件,它可以用于在前端实现拼图验证码功能。 该组件可以用于替代第三方SDK的拼图验证,避免了引入SDK和后台配合的麻烦。你只需要通过npm安装vue-puzzle-vcode即可开始使用。 使用vue-puzzle-vcode时,你需要在代码中引入Vcode组件并使用它来展示验证模态框。你可以设置show属性来控制模态框的显示与隐藏,通过onSuccess事件来处理用户通过验证后的逻辑,通过onClose事件来处理关闭模态框的逻辑。 在示例中,当用户点击登录按钮时,会触发onSubmit方法,使验证码模态框显示出来。当用户通过验证后,会触发onSuccess方法,你需要在该方法中手动隐藏模态框。当用户点击遮罩层时,会触发onClose方法,你需要在该方法中关闭模态框。 除了以上提到的参数外,vue-puzzle还提供了其他可配置的参数,你可以根据需求进行设置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Vue拼图验证组件使用教程](https://blog.csdn.net/weixin_40121676/article/details/102895804)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [vue 使用vue-puzzle-vcode拼图验证纯前端实现](https://blog.csdn.net/qq_37816525/article/details/102560656)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值