P1074 靶形数独
P1074 靶形数独
题目描述
小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 999 格宽× 999 格高的大九宫格中有 99 9 个 333 格宽× 333 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 111 到 99 9 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)
上图具体的分值分布是:最里面一格(黄色区域)为 101010 分,黄色区域外面的一圈(红色区域)每个格子为 9 9 9 分,再外面一圈(蓝色区域)每个格子为 8 88 分,蓝色区域外面一圈(棕色区域)每个格子为 7 7 7 分,最外面一圈(白色区域)每个格子为 6 6 6 分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和
总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。
由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。
输入输出格式
输入格式:一共 999 行。每行 9 9 9 个整数(每个数都在 0−90-90−9 的范围内),表示一个尚未填满的数独方格,未填的空格用“ 000 ”表示。每两个数字之间用一个空格隔开。
输出格式:输出共 111 行。输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数 −1-1−1 。
输入输出样例
7 0 0 9 0 0 0 0 1 1 0 0 0 0 5 9 0 0 0 0 0 2 0 0 0 8 0 0 0 5 0 2 0 0 0 3 0 0 0 0 0 0 6 4 8 4 1 3 0 0 0 0 0 0 0 0 7 0 0 2 0 9 0 2 0 1 0 6 0 8 0 4 0 8 0 5 0 4 0 1 2
2829
0 0 0 7 0 2 4 5 3 9 0 0 0 0 8 0 0 0 7 4 0 0 0 5 0 1 0 1 9 5 0 8 0 0 0 0 0 7 0 0 0 0 0 2 5 0 3 0 5 7 9 1 0 8 0 0 0 6 0 1 0 0 0 0 6 0 9 0 0 0 0 1 0 0 0 0 0 0 0 0 6
2852
说明
【数据范围】
40%的数据,数独中非 000 数的个数不少于 30 3030 。
80%的数据,数独中非 000 数的个数不少于 262626 。
100%的数据,数独中非 00 0 数的个数不少于 24 2424 。
NOIP 2009 提高组 第四题
————————————————————————————————————————————————————————————————
大暴搜啊
普通做法判断行,列,宫,跑的贼慢。
舞蹈链可做……然而不会啊
= 打成 == 调了一个小时。。。码力不足QWQ
我可能要练一练暴搜和模拟了QWQ
code
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; const int f[10][10] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 6, 6, 6, 6, 6, 6, 6, 6, 6}, {0, 6, 7, 7, 7, 7, 7, 7, 7, 6}, {0, 6, 7, 8, 8, 8, 8, 8, 7, 6}, {0, 6, 7, 8, 9, 9, 9, 8, 7, 6}, {0, 6, 7, 8, 9, 10,9, 8, 7, 6}, {0, 6, 7, 8, 9, 9, 9, 8, 7, 6}, {0, 6, 7, 8, 8, 8, 8, 8, 7, 6}, {0, 6, 7, 7, 7, 7, 7, 7, 7, 6}, {0, 6, 6, 6, 6, 6, 6, 6, 6, 6} }; int m[10][10], toth[10], totl[10], ans = -1; bool hang[10][10], lie[10][10], area[10][10]; int slv(int i, int j) { return (i - 1) / 3 * 3 + 1 + (j - 1) / 3; } void dfs(int x, int y, int stp) { if (stp >= 81) { int temp = 0; for (int i = 1; i <= 9; ++ i) for (int j = 1; j <= 9; ++ j) temp += f[i][j] * m[i][j]; ans = max(ans, temp); return ; } for (int i = 1; i <= 9; ++ i) { if (hang[x][i] || lie[y][i] || area[slv(x, y)][i]) continue; hang[x][i] = true; lie[y][i] = true; area[slv(x, y)][i] = true; toth[x]++; totl[y]++; m[x][y] = i; int maxx = -1, maxy = -1, sx = 0, sy = 0; for (int j = 1; j <= 9; ++ j) if (toth[j] > maxx && toth[j] < 9) maxx = toth[j], sx = j; for (int j = 1; j <= 9; ++ j) if (totl[j] > maxy && !m[sx][j]) maxy = totl[j], sy = j; dfs(sx, sy, stp + 1); hang[x][i] = false; lie[y][i] = false; area[slv(x, y)][i] = false; toth[x]--; totl[y]--; m[x][y] = 0; } } int main() { int sp = 0; for (int i = 1; i <= 9; ++ i) for (int j = 1; j <= 9; ++ j) { scanf("%d", &m[i][j]); if (m[i][j] != 0) { toth[i]++; totl[j]++; hang[i][m[i][j]] = true; lie[j][m[i][j]] = true; area[slv(i, j)][m[i][j]] = true; sp++; } } int maxx = -1, maxy = -1, sx, sy; for (int i = 1; i <= 9; ++ i) if (maxx < toth[i] && toth[i] < 9) maxx = toth[i], sx = i; for (int i = 1; i <= 9; ++ i) if (maxy < totl[i] && !m[sx][i]) maxy = totl[i], sy = i; dfs(sx, sy, sp); printf("%d\n", ans); return 0; }
跑了3000+ms
目前最快的正解跑了100+ms
大佬的代码
#include<bits/stdc++.h> using namespace std; struct Node{short x,y,v;}w[128]; short can_get[128]; inline bool cmp(const Node &a,const Node &b){ if(a.v/9!=b.v/9) return a.v<b.v; if(a.x!=b.x) return a.x<b.x; return a.v<b.v; } short zt[16][4]; short done[16][16],be[16][16]; short fz[16][16]={ {6,6,6,6,6,6,6,6,6}, {6,7,7,7,7,7,7,7,6}, {6,7,8,8,8,8,8,7,6}, {6,7,8,9,9,9,8,7,6}, {6,7,8,9,10,9,8,7,6}, {6,7,8,9,9,9,8,7,6}, {6,7,8,8,8,8,8,7,6}, {6,7,7,7,7,7,7,7,6}, {6,6,6,6,6,6,6,6,6}, }; short qz[16][16]; short nex[1024],pre[1024]; int bes,now,bs; inline void dfs(register int p){ if(p==81){ bes=max(bes,now); return; } if(done[w[p].x][w[p].y]){ now+=fz[w[p].x][w[p].y]*done[w[p].x][w[p].y]; dfs(p+1); return; } if((can_get[p]<<3)+can_get[p]+now<=bes) return; ++bs; if(bs>=1900000) return; register int x=w[p].x,y=w[p].y,bel=be[x][y],can_use=zt[x][0]&zt[y][1]&zt[bel][2],t,z; if(fz[x][y]<=6){ while(can_use){ t=pre[can_use]; z=(1<<t); zt[x][0]^=z; zt[y][1]^=z; zt[bel][2]^=z; now+=fz[x][y]*(t+1); dfs(p+1); now-=fz[x][y]*(t+1); zt[x][0]^=z; zt[y][1]^=z; zt[bel][2]^=z; can_use^=z; } } else{ while(can_use){ t=nex[can_use]; z=(1<<t); zt[x][0]^=z; zt[y][1]^=z; zt[bel][2]^=z; now+=fz[x][y]*(t+1); dfs(p+1); now-=fz[x][y]*(t+1); zt[x][0]^=z; zt[y][1]^=z; zt[bel][2]^=z; can_use^=z; } } } int main(){ for(register int i=0;i<9;i++) for(register int j=0;j<3;j++) zt[i][j]=(1<<9)-1; for(register int i=0;i<9;i++) for(register int j=0;j<9;j++) scanf("%d",&done[i][j]); for(register int i=0;i<9;i++){ for(register int j=0;j<i;j++){ swap(done[i][j],done[j][i]); } } for(register int i=0;i<9;i++) for(register int j=0;j<9;j++){ register int a=done[i][j]; if(!a) continue; a--; zt[i][0]-=(1<<a); zt[j][1]-=(1<<a); zt[(i/3)*3+j/3][2]-=(1<<a); } for(register int i=0;i<9;i++){ for(register int j=0;j<9;j++){ register int can_use=zt[i][0]&zt[j][1]&zt[(i/3)*3+j/3][2]; w[i*9+j].x=i,w[i*9+j].y=j; if(done[i][j]) w[i*9+j].v=0; else{ for(register int l=0;l<9;l++){ if((1<<l)&zt[i][0]) w[i*9+j].v+=9; } for(register int l=0;l<9;l++){ if((1<<l)&can_use) w[i*9+j].v++; } } be[i][j]=(i/3)*3+j/3; } } sort(w,w+81,cmp); for(register int i=80;i>=0;i--){ can_get[i]=can_get[i+1]+fz[w[i].x][w[i].y]; } for(register int i=1;i<(1<<9);i++){ for(register int j=0;j<9;j++) if((1<<j)&i) nex[i]=j; for(register int j=8;j>=0;j--) if((1<<j)&i) pre[i]=j; } dfs(0); if(bes==0) puts("-1"); else printf("%d\n",bes); return 0; }
emmmm……用了位运算……
基本做法差不多,但是多了几个剪枝。
这一句怎么看都很玄学
if(bs>=1900000) return;
至今仍搞不懂原理
剪枝……真玄学啊QWQ