P9903 [COCI 2023/2024 #1] Sudoku【数独】

题意

给出一个 13 × 13 13\times 13 13×13矩阵(实际是 9 × 9 9\times 9 9×9的,但是旁边有多余的输入),问这个数独是否符合条件

条件:每一,每一,每一个九宫格,都有数字 1 − 9 1-9 19 不能有重复的数字

输入格式&输入样例

样例中只有.0~9有用,其他可以看做多余的

1.样例输入1
+---+---+---+
|52.|...|.81|
|.39|58.|...|
|.8.|.9.|...|
+---+---+---+
|24.|...|1.3|
|1..|43.|86.|
|.63|..7|.24|
+---+---+---+
|...|1.9|35.|
|..8|.74|6..|
|31.|86.|7.9|
+---+---+---+
2.样例输出1
OK

样例1解释:这个数独没有重复的,所以输出OK

3.样例输入2
+---+---+---+
|3..|6..|..4|
|4.9|8.1|..7|
|..7|.49|6..|
+---+---+---+
|946|157|8.2|
|.2.|3..|745|
|.7.|28.|...|
+---+---+---+
|...|4..|..5|
|8.5|.6.|.2.|
|734|..8|5..|
+---+---+---+
4.样例输出2
GRESKA

样例2解释:第 9 9 9 列数字 5 5 5 出现了 2 2 2 次,且右下角的一个九宫格中的数字 5 5 5 出现了 2 2 2

5.样例输入3
+---+---+---+
|5..|98.|67.|
|6..|...|.31|
|.2.|613|.4.|
+---+---+---+
|.96|8.2|1.7|
|.28|..5|.9.|
|7.3|19.|6..|
+---+---+---+
|962|.7.|.1.|
|1.5|...|76.|
|.7.|5..|9..|
+---+---+---+
6.样例输出3
GRESKA

样例3解释:第 2 2 2 列数字 2 2 2 出现了 2 2 2 次,且第 7 7 7 列数字 6 6 6 出现了

具体code & 解释

1.如何统计每个数的个数

可以使用桶排序去重的方法来进行预处理

如果有数的个数>1,就可以证明这个数独并不合法

详见:去重算法(桶排序去重)

2.如何遍历九宫格

只需以起始坐标为原点,遍历一个 3 × 3 3\times 3 3×3的正方形即可
在这里插入图片描述
其中的九宫格起点,就是起始坐标

在数独里起始坐标有(1,1),(1,4),(1,7),(4,1),(4,4),(4,7),(7,1),(7,4),(7,7)

code↓

#include <bits/stdc++.h>
using namespace std;
char a[15][15];
int c[15][15],x=1,y=0,vis[15]={},vis2[15]={},vis3[15]={};//vis[] 是统计横排有无重复的数组,vis2[] 是统计数列有无重复的数组,vis3[] 是统计九宫格内有无重复的数组
int jgg(int x,int y){//九宫格左上角作为起始的数的坐标
	for(int i=0;i<=2;i++){
		for(int j=0;j<=2;j++){//如实在没看懂,可看上图 ↑
			vis3[c[x+i][y+j]]++;//统计c[x][y],c[x][y+1],c[x][y+2],c[x+1][y],c[x+1][y+1],c[x+1][y+2],c[x+2][y],c[x+2][y+1],c[x+2][y+2]
		}//遍历统计九宫格内有无重复的数出现的预处理
	}
	for(int i=1;i<=9;i++){
		if(vis3[i]>1) return 1;//统计九宫格内是否有重复的数出现,如果有就可以跳到第33行
	}
	memset(vis3,0,sizeof(vis3));//将数组全部归0,避免下一次重复计算
	return 0;//函数正常运行,跳到第30行
}
int pass(){//检查数独是否成立的函数
	for(int i=1;i<=9;i++){
		for(int j=1;j<=9;j++){
			vis[c[i][j]]++;//遍历统计竖列内有无重复的数出现的预处理
			vis2[c[j][i]]++;//遍历统计横排内有无重复的数出现的预处理
		}
		for(int j=1;j<=9;j++){
			if(vis[j]>1) return 1;//如果有重复的,直接返回1
			if(vis2[j]>1) return 1;//同上
		}
		memset(vis,0,sizeof(vis));//将数组全部归0,避免下一次重复计算
		memset(vis2,0,sizeof(vis2));//同上
	}
	if(jgg(1,1)==0&&jgg(1,4)==0&&jgg(1,7)==0&&jgg(4,1)==0&&jgg(4,4)==0&&jgg(4,7)==0&&jgg(7,1)==0&&jgg(7,4)==0&&jgg(7,7)==0){//九宫格所有的起始坐标
		return 0;//返回0代表成立
	}
	else{
		return 1;//有某一个九宫格内有重复的,数独不成立,返回答案1
	}
}
int main(){
	freopen("sudoku.in","r",stdin);
	freopen("sudoku.out","w",stdout);
	for(int i=1;i<=13;i++){
		for(int j=1;j<=13;j++){
			cin>>a[i][j];
			if(a[i][j]-'0'<=9&&a[i][j]-'0'>=0){
				c[x][++y]=a[i][j]-'0';//因为输入格式的问题,所以用x,y来存储坐标
			}
			if(a[i][j]=='.'){
				c[x][++y]=0;
			}
		}
		y=0;//将坐标赋值为0
		x++;//每一次行数+1
		if(a[i][2]=='-') x--;//如果输入的是空行,那么x应该-1 
	}
	if(pass()==0){//如果数独成立,输出"OK"
		cout<<"OK";
		return 0;
	}
	else{
		cout<<"GRESKA";//否则,输出"GRESKA"
		return 0;
	}
	return 0;
}
  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 题目描述: Eko 有一排树,每棵树的高度不同。他想要砍掉一些树,使得剩下的树的高度都相同。他希望砍掉的树的高度尽可能地少,你能帮他算出最少要砍掉多少棵树吗? 输入格式: 第一行包含两个整数 N 和 M,分别表示树的数量和 Eko 希望的树的高度。 第二行包含 N 个整数,表示每棵树的高度。 输出格式: 输出一个整数,表示最少要砍掉的树的数量。 输入样例: 9 5 2 3 4 7 8 9 10 11 12 输出样例: 3 解题思路: 二分答案 首先,我们可以发现,如果我们知道了 Eko 希望的树的高度,那么我们就可以计算出砍掉多少棵树。 具体来说,我们可以遍历每棵树,如果它的高度大于 Eko 希望的树的高度,那么就将它砍掉,否则就保留它。 然后,我们可以使用二分答案的方法来确定 Eko 希望的树的高度。 具体来说,我们可以将树的高度排序,然后二分一个可能的 Eko 希望的树的高度,然后计算砍掉多少棵树,如果砍掉的树的数量小于等于 M,那么说明 Eko 希望的树的高度可能更小,否则说明 Eko 希望的树的高度可能更大。 最后,我们可以得到最少要砍掉的树的数量。 时间复杂度:O(NlogN)。 参考代码: ### 回答2: 这道题目是一道模拟题,需要模拟机器人的移动过程以及得出最终机器人的位置和朝向。首先需要明确机器人的起始位置以及朝向,其次需要读取输入的指令,根据指令逐步移动机器人,并顺便判断是否会越界或者碰到障碍物。最后输出最终机器人的位置和朝向。 在本题中,需要按照从西向东、从北向南、从东向西、从南向北的顺序判断机器人的朝向。为了方便表述,我把机器人的朝向表示为0、1、2、3,分别代表从西向东、从北向南、从东向西、从南向北。 具体地说,机器人按照指令逐步移动时需要分情况讨论,比如: 1.当前机器人朝向为0,即从西向东: 若指令为F,则x坐标+1,但需要判断是否越界或者碰到障碍物。 若指令为L,则朝向变为3。 若指令为R,则朝向变为1。 2.当前机器人朝向为1,即从北向南: 若指令为F,则y坐标-1,但需要判断是否越界或者碰到障碍物。 若指令为L,则朝向变为0。 若指令为R,则朝向变为2。 3.当前机器人朝向为2,即从东向西: 若指令为F,则x坐标-1,但需要判断是否越界或者碰到障碍物。 若指令为L,则朝向变为1。 若指令为R,则朝向变为3。 4.当前机器人朝向为3,即从南向北: 若指令为F,则y坐标+1,但需要判断是否越界或者碰到障碍物。 若指令为L,则朝向变为2。 若指令为R,则朝向变为0。 最后输出最终机器人的位置和朝向即可。 在编写程序时需要注意判断边界和障碍物,以及要用scanf读取输入,不要用C++的cin,否则会TLE。此外,由于本题没有给出边界和障碍物,需要自己设置。最后,本题的思路不难,但是需要认真仔细地处理各种情况,多测试几组数据找出程序的漏洞,这样才能通过本题。 ### 回答3: 本题为一道组合数学题,需要运用排列组合知识进行分析。 题目要求将n个方块填入3*3的网格中,每个方块可以是红色、绿色或蓝色的一个。要求每行、每列和对角线上的方块颜色都不相同。求方案总数。 首先考虑对第一行进行颜色选取。由于第一行每个位置的颜色都不影响其他行和列,故第一行的颜色选取不影响总方案数。所以假设第一行颜色已经确定,考虑第二行的颜色选取。第二行中各位置的颜色受到第一行的限制,只有第一行某位置颜色的补集才能选取。例如,若第一行第一个位置是红色,那么第二行第一个位置不能选取红色。因为每行颜色不能相同,所以第二行受到第一行限制的位置只有3个。第三行同理,由于前两行的限制,只有2个位置可选。做完颜色选取后,再将每行的方块进行排列,此时我们可以使用错排公式得到方案数: D(n) = n!(1 - 1/1! + 1/2! - 1/3! + ... + (-1)^(n)/n!) 最终,方案总数即为每个第一行颜色选取方法下的错排方案数之和。按题意枚举第一行的颜色,就可以得到最终的方案总数了。 总结一下,本题所需要的知识点为:错排公式、颜色限制对组合数的影响、暴力枚举法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花火Spark

鼓励,如星光,照亮我创作的前路

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值