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;
}