NOIP 2009 Senior 4
来源:互联网 发布:java时间换算工具 编辑:程序博客网 时间:2024/06/06 01:40
思路 枚举
思路一:从上到下,从左到右逐个枚举
得分:75分
思路二:填数独的时候,一般的策略都是找可能的解尽量少的地方填,这里我们定义可能的解最少的那一格为最佳填数位置。考虑到要减少常数时间,我就将没有填数的格子找出来,根据能填数的多少排序,再挨个搜索。同时我也想到了最优解剪枝:假设接下的格子每个都能值90分,把他们加上都不足以达到目前已经有的最优解的话说明当前的填法不会有最优解。最后,在初始化的时候可以进行预判,如果给定的数据都非法就不用填了。
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <string>#include <iostream>#include <algorithm>#include <vector>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;inline int readIn(){ int a; scanf("%d", &a); return a;}const int maxn = 15;int rect[maxn][maxn];bool sX[maxn][maxn];bool sY[maxn][maxn];bool sArea[maxn][maxn];int filled;int base;int ans = -1;inline int calcScore(int x, int y, int num){ x = abs(x - 5); y = abs(y - 5); return (10 - std::max(x, y))*num;}inline int area_(int x, int y){ x--; y--; return x / 3 * 3 + y / 3;}struct Empty{ int x; int y; Empty(int x = 0, int y = 0) :x(x), y(y) { } bool operator< (const Empty& b) const //注意排序函数的编写:如果排序函数写错了将会RE { int areaA = area_(x, y); int areaB = area_(b.x, b.y); int ansA = 0; int ansB = 0; for (int i = 1; i <= 9; i++) { if (!sX[x][i] && !sY[y][i] && !sArea[areaA][i]) ansA++; if (!sX[b.x][i] && !sY[b.y][i] && !sArea[areaB][i]) ansB++; } return ansA < ansB; }};std::vector<Empty> empties;void sudoku(int step = 0, int score = base){ if (90 * (81 - filled) + score <= ans) return; //最优性剪枝 if (filled == 81) { ans = std::max(ans, score); return; } int x = empties[step].x, y = empties[step].y; int area = area_(x, y); for (int i = 1; i <= 9; i++) { if (!sX[x][i] && !sY[y][i] && !sArea[area][i]) { rect[x][y] = i; sX[x][i] = sY[y][i] = sArea[area][i] = true; filled++; sudoku(step+1, score + calcScore(x, y, i)); rect[x][y] = 0; filled--; sX[x][i] = sY[y][i] = sArea[area][i] = false; } }}int main(){ bool bOk = true; for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { int x = i; int y = j; int area = area_(x, y); rect[x][y] = readIn(); int& t = rect[x][y]; if (t) { if (!sX[x][t] && !sY[y][t] && !sArea[area][t]) { sX[x][t] = true; sY[y][t] = true; sArea[area][t] = true; filled++; base += calcScore(x, y, t); } else { bOk = false; break; } } } if (!bOk) break; } if (!bOk) { printf("%d\n", ans); return 0; } for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { if (!rect[i][j]) { empties.push_back(Empty(i, j)); } } } std::sort(empties.begin(), empties.end()); sudoku(); printf("%d\n", ans); return 0;}
可惜的是,这样做仍然只有75分。仔细分析后发现:填写了第一个后,就不能保证后面填写的顺序是最优顺序了,最终导致跟没有优化差不多。
思路三:每一次搜索都寻找一次最佳填数位置。
之前没有这么做就是因为想到找到最佳填数位置这一操作可能耗时较多。但事实上这个担心是没有必要的:如果指数级的解答树能减去很大一部分,这一常数时间的花费是相当划算的。
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <string>#include <iostream>#include <algorithm>#include <vector>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;inline int readIn(){ int a; scanf("%d", &a); return a;}const int maxn = 15;int rect[maxn][maxn];bool sX[maxn][maxn];bool sY[maxn][maxn];bool sArea[maxn][maxn];int countX[maxn];int filled;int base;int ans = -1;inline int calcScore(int x, int y, int num) //其实我还想强调一下获取当前的圈数的方法{ x = abs(x - 5); y = abs(y - 5); return (10 - std::max(x, y))*num;}inline int area_(int x, int y){ x--; y--; return x / 3 * 3 + y / 3;}void sudoku(int score = base){ if (90 * (81 - filled) + score <= ans) return; //最优性剪枝 if (filled == 81) { ans = std::max(ans, score); return; } int x = 0, y = 0; int area = 0; int maxVal = 0; //找到能填的数最少的那一个格子 for (int i = 1; i <= 9; i++) { if (countX[i] == 9) continue; //这一行都已经填满了,不用在这一行里找:尽量减少常数时间 for (int j = 1; j <= 9; j++) { if (rect[i][j]) continue; int tempCount = 0; int tempArea = area_(i, j); for (int k = 1; k <= 9; k++) { if (!(!sX[i][k] && !sY[j][k] && !sArea[tempArea][k])) { tempCount++; } } if (tempCount == 9) return; //这个地方什么都不能填了,说明当前已经无解 if (tempCount > maxVal) { x = i; y = j; area = tempArea; maxVal = tempCount; } } } for (int i = 1; i <= 9; i++) { if (!sX[x][i] && !sY[y][i] && !sArea[area][i]) { rect[x][y] = i; sX[x][i] = sY[y][i] = sArea[area][i] = true; filled++; countX[x]++; sudoku(score + calcScore(x, y, i)); rect[x][y] = 0; filled--; countX[x]--; sX[x][i] = sY[y][i] = sArea[area][i] = false; } }}int main(){ bool bOk = true; for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { int x = i; int y = j; int area = area_(x, y); rect[x][y] = readIn(); int& t = rect[x][y]; if (t) { if (!sX[x][t] && !sY[y][t] && !sArea[area][t]) { sX[x][t] = true; sY[y][t] = true; sArea[area][t] = true; countX[x]++; filled++; base += calcScore(x, y, t); } else { bOk = false; //进行预判,如果给定的数据都已经非法,则不要填数了 break; } } } if (!bOk) break; } if(bOk) sudoku(); printf("%d\n", ans); return 0;}
光说搜索也不是特别难,但写的时候顾虑太多了。所以建议还是先写出来再说。有些时候可能写出来优化后还要慢一些(比如思路二),而对拍又很难检测出来(合法数据太难编了)。所以要先建立理论,再坚定不移地编写代码,不要在开始写了过后犹豫。
阅读全文
0 0
- NOIP 2009 Senior 4
- NOIP 2009 Senior 1
- NOIP 2009 Senior 3
- NOIP 2011 Senior 4
- NOIP 2015 Senior 4
- NOIP 2013 Senior 4
- NOIP 2016 Senior 4
- NOIP 2008 Senior 4
- NOIP 2007 Senior 4
- NOIP 2011 Senior 2
- NOIP 2011 Senior 3
- NOIP 2011 Senior 5
- NOIP 2011 Senior 6
- NOIP 2012 Senior 2
- NOIP 2012 Senior 5
- NOIP 2012 Senior 3
- NOIP 2015 Senior 2
- NOIP 2015 Senior 3
- C语言之冒泡排序算法
- 正则表达式re中的group和groups
- 华丽分割线
- JAVA for循环、函数重载、return关键字 day4
- 非常好用的底部导航栏
- NOIP 2009 Senior 4
- sklearn dataset模块学习
- 简单了解weblogic配置文件
- C++中string.find()函数与string::npos
- 115个Java面试题和答案——终极列表(下)
- 【0055】mysql触发器
- DevBySimon-RecyclerView
- android 自定义裁剪View
- HDFS文件读取与写入剖析