noip2009靶形数独

题目描述

小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他

们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,

Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。

靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有 9 个 3 格宽×3 格

高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 1 到 9 的数字。每个数字在每个小九宫格内不能

重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即

每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)

上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红

色区域)每个格子为 9 分,再外面一圈(蓝色区域)每个格子为 8 分,蓝色区域外面一圈(棕

色区域)每个格子为 7 分,最外面一圈(白色区域)每个格子为 6 分,如上图所示。比赛的

要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取

更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字

的乘积的总和

总分数即每个方格上的分值和完成这个数独时填在相应格上的数字

的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。

由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能

够得到的最高分数。

输入输出格式

输入格式:

一共 9 行。每行 9 个整数(每个数都在 0―9 的范围内),表示一个尚未填满的数独方

格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。

输出格式:

输出文件 sudoku.out 共 1 行。

输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1。

输入输出样例

输入样例#1:
sudoku1
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

sudoku2
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
输出样例#1:
sudoku1
2829

sudoku2
2852

说明

【数据范围】

40%的数据,数独中非 0 数的个数不少于 30。

80%的数据,数独中非 0 数的个数不少于 26。

100%的数据,数独中非 0 数的个数不少于 24。

NOIP 2009 提高组 第四题


此题是一道很有意思的搜索题,只是要剪枝的技巧运用的很熟练,

相信若是没有分数的限制,光是求数独,大家应该都会做,

用一个行的数组row[i][j]表示第i行j用没用过,列的数组,块的数组分别判断

60分做法:

从1,1开始搜,若有数字则往下一个搜,若没有数字则枚举一到九的取值,爆搜可以的:

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x<y)return y;
    return x;
}
int minn(int x,int y){
    if(x<y)return x;
    return y;
}
int abss(int x){
    if(x>=0)return x;
    return -x;
}
const int maxn = 100010;
int a[20][20],mark[20][20];
int row[20][20],column[20][20];
int p[20][20],vis[20][20];
int ans;
int sum;
bool flag;
int t[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};
void dfs(int x,int y){
    if(x==10){
        flag=1;
        sum=maxx(sum,ans);
        return;
    }
    if(a[x][y]){
        if(y==9){
            dfs(x+1,1);
        }
        else {
            dfs(x,y+1);
        }
        return;
    }
    int i,j;
    For(i,1,9){
        int k=(x-1)/3*3+(y-1)/3+1;//计算在第几块
        if(!row[x][i]&&!column[y][i]&&!p[k][i]){
            row[x][i]=1;column[y][i]=1;p[k][i]=1;
            ans+=i*t[x][y];
            if(y==9){
                dfs(x+1,1);
            }
            else {
                dfs(x,y+1);
            }
            ans-=i*t[x][y];
            row[x][i]=0;column[y][i]=0;p[k][i]=0;
        }
    }
}
int main(){
    int i,j;
    For(i,1,9){
        For(j,1,9){
            a[i][j]=read();
            if(a[i][j]){
                ans+=a[i][j]*t[i][j];
                vis[i][j]=1;
                row[i][a[i][j]]=1;column[j][a[i][j]]=1;
                p[(i-1)/3*3+(j-1)/3+1][a[i][j]]=1;
            }
        }
    }
    dfs(1,1);
    if(!flag)printf("-1\n");
    else {
        printf("%d\n",sum);
    }
    return 0;
}
65分做法: 不打那个表不知为何还多拿了5分,这5分也就是常数问题

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x<y)return y;
    return x;
}
int minn(int x,int y){
    if(x<y)return x;
    return y;
}
int abss(int x){
    if(x>=0)return x;
    return -x;
}
const int maxn = 100010;
int a[20][20],mark[20][20];
int row[20][20],column[20][20];
int p[20][20],vis[20][20];
int ans[20];
int sum;
bool flag;
int d[5]={6,7,8,9,10};
void dfs(int x,int y){
    if(x==10){
        flag=1;
        int i,he=0;
        For(i,0,4)he+=ans[i]*d[i];//由于ans[i]只是记录了在d[i]的分值范围不乘d[i]的总分是多少
        sum=maxx(sum,he);
        return;
    }
    int ok=-1;
    if(a[x][y]){
        if(y==9){
            dfs(x+1,1);
        }
        else {
            dfs(x,y+1);
        }
        return;
    }
    if(x==1||x==9||y==1||y==9)ok=0;//这是一系列冗长的判断位于哪个分值区域,后面才发现毫无意义
    else if(((x==2||x==8)&&(y>=2&&y<=8))||((x!=1&&x!=9)&&(y==2||y==8)))ok=1;
    else if(((x==3||x==7)&&(y>=3&&y<=7))||((x>=3&&x<=7)&&(y==3||y==7)))ok=2;
    else if(((x==4||x==6)&&(y>=4&&y<=6))||((x==5)&&(y==4||y==6)))ok=3;
    else if(x==5&&y==5)ok=4;
    int i,j;
    For(i,1,9){
        if(!row[x][i]&&!column[y][i]&&!p[(x-1)/3*3+(y-1)/3+1][i]){
            row[x][i]=1;column[y][i]=1;p[(x-1)/3*3+(y-1)/3+1][i]=1;
            ans[ok]+=i;
            if(y==9){
                dfs(x+1,1);
            }
            else {
                dfs(x,y+1);
            }
            ans[ok]-=i;
            row[x][i]=0;column[y][i]=0;p[(x-1)/3*3+(y-1)/3+1][i]=0;
        }
    }
}
int main(){
    int i,j;
    For(i,1,9){
        For(j,1,9){
            a[i][j]=read();
            if(a[i][j]){
                int ok;
                if(i==1||i==9||j==1||j==9)ok=0;
                else if(((i==2||i==8)&&(j>=2&&j<=8))||((i!=1&&i!=9)&&(j==2||j==8)))ok=1;
                else if(((i==3||i==7)&&(j>=3&&j<=7))||((i>=3&&i<=7)&&(j==3||j==7)))ok=2;
                else if(((i==4||i==6)&&(j>=4&&j<=6))||((i==5)&&(j==4||j==6)))ok=3;
                else if(i==5&&j==5)ok=4;
                ans[ok]+=a[i][j];
                vis[i][j]=1;
                row[i][a[i][j]]=1;column[j][a[i][j]]=1;
                p[(i-1)/3*3+(j-1)/3+1][a[i][j]]=1;
            }
        }
    }
    dfs(1,1);
    if(!flag)printf("-1\n");
    else {
        printf("%d\n",sum);
    }
    return 0;
}
70分做法:把ans这个数组改成单个变量,就像60分做法那样,却又不知为何多了五分

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x<y)return y;
    return x;
}
int minn(int x,int y){
    if(x<y)return x;
    return y;
}
int abss(int x){
    if(x>=0)return x;
    return -x;
}
const int maxn = 100010;
int a[20][20],mark[20][20];
int row[20][20],column[20][20];
int p[20][20],vis[20][20];
int ans;
int sum;
bool flag;
int d[5]={6,7,8,9,10};
void dfs(int x,int y){
    if(x==10){
        flag=1;
        sum=maxx(sum,ans);//就只有这里变了
        return;
    }
    int ok=-1;
    if(a[x][y]){
        if(y==9){
            dfs(x+1,1);
        }
        else {
            dfs(x,y+1);
        }
        return;
    }
    if(x==1||x==9||y==1||y==9)ok=0;
    else if(((x==2||x==8)&&(y>=2&&y<=8))||((x!=1&&x!=9)&&(y==2||y==8)))ok=1;
    else if(((x==3||x==7)&&(y>=3&&y<=7))||((x>=3&&x<=7)&&(y==3||y==7)))ok=2;
    else if(((x==4||x==6)&&(y>=4&&y<=6))||((x==5)&&(y==4||y==6)))ok=3;
    else if(x==5&&y==5)ok=4;
    int i,j;
    For(i,1,9){
        if(!row[x][i]&&!column[y][i]&&!p[(x-1)/3*3+(y-1)/3+1][i]){
            row[x][i]=1;column[y][i]=1;p[(x-1)/3*3+(y-1)/3+1][i]=1;
            ans+=i*d[ok];
            if(y==9){
                dfs(x+1,1);
            }
            else {
                dfs(x,y+1);
            }
            ans-=i*d[ok];
            row[x][i]=0;column[y][i]=0;p[(x-1)/3*3+(y-1)/3+1][i]=0;
        }
    }
}
int main(){
    int i,j;
    For(i,1,9){
        For(j,1,9){
            a[i][j]=read();
            if(a[i][j]){
                int ok;
                if(i==1||i==9||j==1||j==9)ok=0;
                else if(((i==2||i==8)&&(j>=2&&j<=8))||((i!=1&&i!=9)&&(j==2||j==8)))ok=1;
                else if(((i==3||i==7)&&(j>=3&&j<=7))||((i>=3&&i<=7)&&(j==3||j==7)))ok=2;
                else if(((i==4||i==6)&&(j>=4&&j<=6))||((i==5)&&(j==4||j==6)))ok=3;
                else if(i==5&&j==5)ok=4;
                ans+=a[i][j]*d[ok];
                vis[i][j]=1;
                row[i][a[i][j]]=1;column[j][a[i][j]]=1;
                p[(i-1)/3*3+(j-1)/3+1][a[i][j]]=1;
            }
        }
    }
    dfs(1,1);
    if(!flag)printf("-1\n");
    else {
        printf("%d\n",sum);
    }
    return 0;
}
经过thk同学的点拨,我终于想起来了搜索剪枝的第一大要义:先从限制多的搜起,这样可以减少分支数量,不信可以好好再想想

激动的我把没遍历过得放到结构体里,并且把每个点的限制预处理出来,这个位置的行有一个已经有数的限制就加一,列和块也是一样的,

再sort一遍,从限制最大的开始搜

再按顺序搜,于是就有了另一个65分程序:

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x<y)return y;
    return x;
}
int minn(int x,int y){
    if(x<y)return x;
    return y;
}
int abss(int x){
    if(x>=0)return x;
    return -x;
}
const int maxn = 100010;
int a[20][20],mark[20][20];
int row[20][20],column[20][20];
int p[20][20],vis[20][20];
int ans;
int sum;
bool flag;
struct node{
    int x,y,limit;
}; 
node save[100];
int cntrow[100],cntcolumn[100],cntp[100][100],cntline,cntlie,cntmark[100],cnt;
int t[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};
void dfs(int h){
    if(h==cnt+1){
        flag=1;
        sum=maxx(sum,ans);
        return;
    }
    int x=save[h].x,y=save[h].y;
    int i,j;
    For(i,1,9){
        int k=(x-1)/3*3+(y-1)/3+1;
        if(!row[x][i]&&!column[y][i]&&!p[k][i]){
            row[x][i]=1;column[y][i]=1;p[k][i]=1;
            ans+=i*t[x][y];
            dfs(h+1);
            ans-=i*t[x][y];
            row[x][i]=0;column[y][i]=0;p[k][i]=0;
        }
    }
}
bool cmp(node c,node d){
    return c.limit>d.limit;
}
int main(){
    int i,j,k;
    For(i,1,9){
        For(j,1,9){
            a[i][j]=read();
            if(a[i][j]){
                int jishu=(i-1)/3*3+(j-1)/3+1;
                ans+=a[i][j]*t[i][j];
                vis[i][j]=1;
                row[i][a[i][j]]=1;column[j][a[i][j]]=1;
                p[jishu][a[i][j]]=1;
                For(k,1,cntline)save[cntrow[k]].limit++;//记录这一行若是有没有数的位置,把这个数的限制加一
                For(k,1,cntlie)save[cntcolumn[k]].limit++;//同上
                For(k,1,cntmark[jishu])save[cntp[jishu][k]].limit++;
            }
            else{
                save[++cnt].x=i,save[cnt].y=j;
                cntrow[++cntline]=cnt;
                cntcolumn[++cntlie]=cnt;
                int jishu=(i-1)/3*3+(j-1)/3+1;
                cntp[jishu][++cntmark[jishu]]=cnt;
            }
        }
    }
    sort(save+1,save+cnt+1,cmp);
    dfs(1);
    if(!flag)printf("-1\n");
    else {
        printf("%d\n",sum);
    }
    return 0;
}
//然后我就发现自己又智障了,不仅在边输入时边预处理提高了复杂度,而且还不能把限制正确安插到每一个没有数的点;
所以其实可以去掉很多无用的步骤,于是就有了85分程序
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x<y)return y;
    return x;
}
int minn(int x,int y){
    if(x<y)return x;
    return y;
}
int abss(int x){
    if(x>=0)return x;
    return -x;
}
const int maxn = 100010;
int a[20][20],mark[20][20];
int row[20][20],column[20][20];
int p[20][20],vis[20][20];
int ans;
int sum;
bool flag;
struct node{
    int x,y,limit;
}; 
node save[100];
int cntrow[100],cntcolumn[100],cntp[100],cntline,cntlie,cntmark[100],cnt;
int t[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 th[10][10]={
    0,0,0,0,0,0,0,0,0,0
    ,0,1,1,1,2,2,2,3,3,3
    ,0,1,1,1,2,2,2,3,3,3
    ,0,1,1,1,2,2,2,3,3,3
    ,0,4,4,4,5,5,5,6,6,6
    ,0,4,4,4,5,5,5,6,6,6
    ,0,4,4,4,5,5,5,6,6,6
    ,0,7,7,7,8,8,8,9,9,9
    ,0,7,7,7,8,8,8,9,9,9
    ,0,7,7,7,8,8,8,9,9,9
};
void dfs(int h){
    if(h==cnt+1){
        flag=1;
        sum=maxx(sum,ans);
        return;
    }
    int x=save[h].x,y=save[h].y;
    int i,j;
    For(i,1,9){
        if(!row[x][i]&&!column[y][i]&&!p[th[x][y]][i]){
            row[x][i]=1;column[y][i]=1;p[th[x][y]][i]=1;
            ans+=i*t[x][y];
            dfs(h+1);//同样按顺序搜
            ans-=i*t[x][y];
            row[x][i]=0;column[y][i]=0;p[th[x][y]][i]=0;
        }
    }
}
bool cmp(node c,node d){
    return c.limit>d.limit;
}
int main(){
    int i,j,k;
    For(i,1,9){
        For(j,1,9){
            a[i][j]=read();
            if(a[i][j]){
                ans+=a[i][j]*t[i][j];
                vis[i][j]=1;
                row[i][a[i][j]]=1;column[j][a[i][j]]=1;
                p[th[i][j]][a[i][j]]=1;
                cntrow[i]++;cntcolumn[j]++;cntp[th[i][j]]++;//这一行若有,则这一行的限制加加,列,块同样
            }
            else{
                save[++cnt].x=i,save[cnt].y=j;//这里只记录
            }
        }
    }
    For(i,1,cnt){
        int x=save[i].x,y=save[i].y;
        save[i].limit+=cntrow[x]+cntcolumn[y]+cntp[th[x][y]];//这里就把这个位置行、列、块的限制都加上,排序
    }
    sort(save+1,save+cnt+1,cmp);
    dfs(1);
    if(!flag)printf("-1\n");
    else {
        printf("%d\n",sum);
    }
    return 0;
}
然后我再把ans这单一变量改成最开始那个65分程序的ans数组,于是又多了5分,现在离成功只有5分之遥:
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x<y)return y;
    return x;
}
int minn(int x,int y){
    if(x<y)return x;
    return y;
}
int abss(int x){
    if(x>=0)return x;
    return -x;
}
const int maxn = 100010;
int a[20][20],mark[20][20];
int row[20][20],column[20][20];
int p[20][20],vis[20][20];
int ans[20];
int sum;
bool flag;
struct node{
    int x,y,limit;
}; 
node save[100];
int cntrow[100],cntcolumn[100],cntp[100],cntline,cntlie,cntmark[100],cnt;
int t[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 th[10][10]={
    0,0,0,0,0,0,0,0,0,0
    ,0,1,1,1,2,2,2,3,3,3
    ,0,1,1,1,2,2,2,3,3,3
    ,0,1,1,1,2,2,2,3,3,3
    ,0,4,4,4,5,5,5,6,6,6
    ,0,4,4,4,5,5,5,6,6,6
    ,0,4,4,4,5,5,5,6,6,6
    ,0,7,7,7,8,8,8,9,9,9
    ,0,7,7,7,8,8,8,9,9,9
    ,0,7,7,7,8,8,8,9,9,9
};
void dfs(int h){
    if(h==cnt+1){
        int u;
        flag=1;
        int he=0;
        For(u,6,10){//也就这里有些改变
            he+=ans[u]*u;
        }
        sum=maxx(sum,he);
        return;
    }
    int x=save[h].x,y=save[h].y;
    int i,k=th[x][y],zhi=t[x][y];
    For(i,1,9){
        if(!row[x][i]&&!column[y][i]&&!p[k][i]){
            row[x][i]=1;column[y][i]=1;p[k][i]=1;
            ans[zhi]+=i;
            dfs(h+1);
            ans[zhi]-=i;
            row[x][i]=0;column[y][i]=0;p[k][i]=0;
        }
    }
}
bool cmp(node c,node d){
    return c.limit>d.limit;
}
int main(){
    int i,j,k;
    For(i,1,9){
        For(j,1,9){
            a[i][j]=read();
            if(a[i][j]){
                k=th[i][j];
                ans[t[i][j]]+=a[i][j];
                vis[i][j]=1;
                row[i][a[i][j]]=1;column[j][a[i][j]]=1;
                p[k][a[i][j]]=1;
                ++cntrow[i];++cntcolumn[j];++cntp[k];
            }
            else{
                save[++cnt].x=i,save[cnt].y=j;
            }
        }
    }
    For(i,1,cnt){
        int x=save[i].x,y=save[i].y;
        save[i].limit+=cntrow[x]+cntcolumn[y]+cntp[th[x][y]];
    }
    sort(save+1,save+cnt+1,cmp);
    dfs(1);
    if(!flag)printf("-1\n");
    else {
        printf("%d\n",sum);
    }
    return 0;
}
//然而卡了很久的常数,这最后TLE的这个点却无论如何都差了一点点卡不过去,沮丧的我突然又想起昨天thk说的每次取限制最小的,

我略微思索,突然发现在最开始排好序之后,在搜索之后,每个没搜过的点的限制会发生变化,不能只按照最开始的那个排序的顺序搜,

要每次更新限制,搜限制最大的那个点,以为就要大功告成的我激动地打完了优化了的程序,刚刚T掉的那个点过了,却又莫名其妙的T了另外三个点,

85分的程序就此出现:

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x<y)return y;
    return x;
}
int minn(int x,int y){
    if(x<y)return x;
    return y;
}
int abss(int x){
    if(x>=0)return x;
    return -x;
}
const int maxn = 100010;
int a[20][20],mark[20][20];
int row[20][20],column[20][20];
int p[20][20];
int ans[20];
int sum;
bool flag;
struct node{
    int x,y,limit;
}; 
node save[200];
int cntrow[200],cntcolumn[200],cntp[200],cnt;
int t[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 th[10][10]={
    0,0,0,0,0,0,0,0,0,0
    ,0,1,1,1,2,2,2,3,3,3
    ,0,1,1,1,2,2,2,3,3,3
    ,0,1,1,1,2,2,2,3,3,3
    ,0,4,4,4,5,5,5,6,6,6
    ,0,4,4,4,5,5,5,6,6,6
    ,0,4,4,4,5,5,5,6,6,6
    ,0,7,7,7,8,8,8,9,9,9
    ,0,7,7,7,8,8,8,9,9,9
    ,0,7,7,7,8,8,8,9,9,9
};
bool cmp(node c,node d){
    return c.limit>d.limit;
}
bool vis[100];
void dfs(int h,int zong){
    if(zong==cnt+1){
        int u;
        flag=1;
        int he=0;
        For(u,6,10){
            he+=ans[u]*u;
        }
        sum=maxx(sum,he);
        return;
    }
    int x=save[h].x,y=save[h].y;
    int i,j,k=th[x][y],zhi=t[x][y];
    For(i,1,9){
        if(row[x][i]||column[y][i]||p[k][i])continue;
        row[x][i]=1;column[y][i]=1;p[k][i]=1;
        ans[zhi]+=i;
        ++cntrow[x],++cntcolumn[y],++cntp[k];//在这里把这一列,行,块的限制全部加一
        int maxxx=0,end=0;
        For(j,1,cnt){
            if(!vis[j]){//vis数组用来标记访没访问过
                int v=save[j].x,w=save[j].y;
                save[j].limit=cntrow[v]+cntcolumn[w]+cntp[th[v][w]];//更新
                if(save[j].limit>maxxx)maxxx=save[j].limit,end=j;//找限制最大的
            }
        }
        vis[end]=1;
        dfs(end,zong+1);
        vis[end]=0;
        ans[zhi]-=i;
        row[x][i]=0;column[y][i]=0;p[k][i]=0;
        --cntrow[x],--cntcolumn[y],--cntp[k];
    }
}
int main(){
    int i,j,k;
    For(i,1,9){
        For(j,1,9){
            a[i][j]=read();
            k=th[i][j];
            if(a[i][j]){    
                ans[t[i][j]]+=a[i][j];
                row[i][a[i][j]]=1;column[j][a[i][j]]=1;
                p[k][a[i][j]]=1;
                ++cntrow[i];++cntcolumn[j];++cntp[k];
            }
            else{
                save[++cnt].x=i,save[cnt].y=j;
            }
        }
    }
    For(i,1,cnt){
        int x=save[i].x,y=save[i].y;
        save[i].limit=cntrow[x]+cntcolumn[y]+cntp[th[x][y]];
    }
    sort(save+1,save+cnt+1,cmp);
    vis[1]=1;
    dfs(1,1);
    if(!flag)printf("-1\n");
    else {
        printf("%d\n",sum);
    }
    return 0;
}
再次受挫的我百思不得其解,只好再次向thk请教,终于发现自己判断限制是多么的愚昧!其实只要看一开始没有数的那个位置有多少数可以选,

能选的数越少说明限制越多,但如果不边搜边更新,也最多只有95分,在我一通怒敲键盘之后,终于A掉了!

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x<y)return y;
    return x;
}
int minn(int x,int y){
    if(x<y)return x;
    return y;
}
int abss(int x){
    if(x>=0)return x;
    return -x;
}
const int maxn = 100010;
int a[20][20],mark[20][20];
int row[20][20],column[20][20];
int p[20][20],vis[1000];
int ans[20];
int sum;
bool flag;
struct node{
    int x,y,limit;
}; 
node save[100];
int cntrow[100],cntcolumn[100],cntp[100],cntline,cntlie,cntmark[100],cnt;
int t[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 th[10][10]={
    0,0,0,0,0,0,0,0,0,0
    ,0,1,1,1,2,2,2,3,3,3
    ,0,1,1,1,2,2,2,3,3,3
    ,0,1,1,1,2,2,2,3,3,3
    ,0,4,4,4,5,5,5,6,6,6
    ,0,4,4,4,5,5,5,6,6,6
    ,0,4,4,4,5,5,5,6,6,6
    ,0,7,7,7,8,8,8,9,9,9
    ,0,7,7,7,8,8,8,9,9,9
    ,0,7,7,7,8,8,8,9,9,9
};
void dfs(int h,int zong){
    if(zong==cnt+1){
        int u;
        flag=1;
        int he=0;
        For(u,6,10){
            he+=ans[u]*u;
        }
        sum=maxx(sum,he);
        return;
    }
    int x=save[h].x,y=save[h].y;
    int i,j,k=th[x][y],r,zhi=t[x][y];
    For(i,1,9){
        if(!row[x][i]&&!column[y][i]&&!p[k][i]){
            row[x][i]=1;column[y][i]=1;p[k][i]=1;
            ans[zhi]+=i;
            int minnn=inf,end=0;
            For(j,1,cnt){//也就把这里改了,判断限制通过看有几个可以选的,选能选的最少的那个
                if(vis[j])continue;
                int hee=0;
                int v=save[j].x,w=save[j].y;
                For(r,1,9){
                    if(!row[v][r]&&!column[w][r]&&!p[th[v][w]][r]){
                        hee++;
                    }
                }
                if(hee<minnn)minnn=hee,end=j;
            }
            vis[end]=1;
            dfs(end,zong+1);
            vis[end]=0;
            ans[zhi]-=i;
            row[x][i]=0;column[y][i]=0;p[k][i]=0;
        }
    }
}
bool cmp(node c,node d){
    return c.limit>d.limit;
}
int main(){//这下面我还是没改
    int i,j,k;
    For(i,1,9){
        For(j,1,9){
            a[i][j]=read();
            if(a[i][j]){
                k=th[i][j];
                ans[t[i][j]]+=a[i][j];
                row[i][a[i][j]]=1;column[j][a[i][j]]=1;
                p[k][a[i][j]]=1;
                ++cntrow[i];++cntcolumn[j];++cntp[k];
            }
            else{
                save[++cnt].x=i,save[cnt].y=j;
            }
        }
    }
    For(i,1,cnt){
        int x=save[i].x,y=save[i].y;
        save[i].limit+=cntrow[x]+cntcolumn[y]+cntp[th[x][y]];
    }
    sort(save+1,save+cnt+1,cmp);
    vis[1]=1;
    dfs(1,1);
    if(!flag)printf("-1\n");
    else {
        printf("%d\n",sum);
    }
    return 0;
}
回首向来萧瑟处,傻得要命蠢得不行!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值