NOIP 2009 提高组 复赛 sudoku 靶形数独

本文提供了一种解决NOIP2009提高组复赛靶形数独问题的方法,通过详细的代码解释及算法流程,有效地解决了靶形数独问题,并讨论了代码提交过程中遇到的问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NOIP 2009 提高组 复赛 sudoku 靶形数独

1.该年提高组最后一道(第四道),估计难度极大,代码量100+。

2.//noip2009 靶形数独 P1074 靶形数独
//http://www.cnblogs.com/zbtrs/p/5715422.html此文代码写得够短,值得研究 不过很遗憾,样例都通不过
//http://blog.youkuaiyun.com/yuyanggo/article/details/48711637此文代码写得漂亮,回溯,很像八皇后。开始研究,提交,在落谷中 ,测试点9,13 TLE 
//http://blog.sina.com.cn/s/blog_5d0d0f450100jm6u.html此文代码注释详细,标记一下,提交AC,而且时间效率很高 
//洛谷中遇到math.h必须以C++方式提交,否则,会报编译错误 ,这次以C语言方式提交,发现没有这个问题了。
//第一次提交,测试语句忘记删除,全WA,修改,提交,测试点1 WA ,在主函数里结尾阶段,加上if else  语句,提交AC 2017-9-24 20:42 
#include <stdio.h>
#include <string.h>
#include <math.h>
int a[11][11],r[11],rn[11],cn[11],xj[5][5],blank[11],rank[11],ans=0;//a[][]九宫格数据,r[]标记该行被占用的位,rn[]标记该行使用的数字 cn[]标记该列使用的数字 xj[][]标记该小九宫格使用的数字 blank[]需要填数的位数 
int calculate(){//计算分值
    int i,j,sum=0;
    for(i=1;i<=4;i++){//行处理 
        for(j=i;j<=10-i;j++){
            sum+=a[i][j]*(5+i);
            sum+=a[10-i][j]*(5+i);
        }
    }
    for(j=1;j<=4;j++)//列处理 
        for(i=j+1;i<=9-j;i++){//4 此处写成 for(i=j+1;i<=9-i;i++)
            sum+=a[i][j]*(5+j);
            sum+=a[i][10-j]*(5+j);
        } 
    sum+=a[5][5]*10;//中心处理 
    if(sum>ans)
        ans=sum;
}
void dfs(int k){
    int i,j,pos,x,y,p,z;
    if(k==10){
        calculate();return ;
    }
    i=rank[k];
    x=511-r[i];//r[i]中没有被占用的位,置1 
    y=x&-x;//从r[i]中没有占用的最低位取起
    j=(int)log2(y)+1;//转换为当前位置序号 
    pos=511-(rn[i]|cn[j]|xj[(i-1)/3][(j-1)/3]);//3 此处写成pos=511-(rn[i]|cn[i]|xj[(i-1)/3][(j-1)/3]); 没有被占用的数字,第一位表示1,第二位表示2,... 
    r[i]|=y;
    while(pos){
        p=pos&-pos;//取出没被占用的数字的最低位 
        pos-=p;//剩下没被占用的数字位置 
        z=(int)log2(p)+1;//转换为当前具体使用数字
        rn[i]|=p,cn[j]|=p,xj[(i-1)/3][(j-1)/3]|=p,a[i][j]=z;
        if(x==y) dfs(k+1);//只有1位没被占用,下一行 
        else dfs(k);//当前行 
        rn[i]-=p,cn[j]-=p,xj[(i-1)/3][(j-1)/3]-=p;//回溯 
    }
    r[i]-=y;

int main(){
    int i,j,p;
    memset(a,0,sizeof(a)),memset(r,0,sizeof(r)),memset(rn,0,sizeof(rn)),memset(cn,0,sizeof(cn)),memset(xj,0,sizeof(xj)),memset(blank,0,sizeof(blank));
    for(i=1;i<=9;i++)
        for(j=1;j<=9;j++){
            scanf("%d",&a[i][j]);
            if(a[i][j]){//非0
                r[i]|=1<<(j-1);//j-1位被占用 
                p=1<<(a[i][j]-1);//第一位代表数字1,第二位代表数字2,依次类推 
                if((rn[i]&p)!=0||(cn[j]&p)!=0||(xj[(i-1)/3][(j-1)/3]&p)!=0){//2 此处写成 if(rn[i]&p!=0||cn[j]&p!=0||xj[(i-1)/3][(j-1)/3]&p!=0) 1 此处写成 if(rn[i]&p!=0&&cn[j]&p!=0&&xj[(i-1)/3][(j-1)/3]&p!=0)应是或 
                    printf("-1\n");return 0;
                }
                rn[i]|=p,cn[j]|=p,xj[(i-1)/3][(j-1)/3]|=p;
            }else//0 
                blank[i]++;
        }
    for(i=1;i<=9;i++)
        rank[i]=i;
    for(i=1;i<=9;i++)
        for(j=i+1;j<=9;j++)
            if(blank[rank[i]]>blank[rank[j]]){//自小到大排列
                rank[i]^=rank[j];//位运算方式的交换 
                rank[j]^=rank[i];
                rank[i]^=rank[j];
            } 
    for(i=1;blank[rank[i]]==0;i++);//跳过不需要添数字的行 
    dfs(i);//从最少需要填数字的行开始深搜
    if(ans)//5添加该if else语句 对应测试点1 
        printf("%d\n",ans);
    else
        printf("-1\n"); 
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值