P1162 填涂颜色
题目描述
由数字 00 组成的方阵中,有一任意形状闭合圈,闭合圈由数字 11 构成,围圈时只走上下左右 44 个方向。现要求把闭合圈内的所有空间都填写成 22。例如:6\times 66×6 的方阵(n=6n=6),涂色前和涂色后的方阵如下:
0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 1
输入格式
每组测试数据第一行一个整数 n(1 \le n \le 30)n(1≤n≤30)。
接下来 nn 行,由 00 和 11 组成的 n \times nn×n 的方阵。
方阵内只有一个闭合圈,圈内至少有一个 00。
//感谢黄小U饮品指出本题数据和数据格式不一样. 已修改(输入格式)
输出格式
已经填好数字 22 的完整方阵。
输入输出样例
输入 #1复制
6 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1输出 #1复制
0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 1说明/提示
对于 100\%100% 的数据,1 \le n \le 301≤n≤30。
刚看到这个题目的时候,一般都会想到DFS,但是我还想到一个当时感觉非常正确的解法,
就是遍历地图上每一个点,然后再遍历它的四个方向,如果四个方向都有1,那这个数就是被包围的。
#include<stdio.h>
int a[35][35], b[35][35],cnt=0,n;
int f(int x, int y)
{
cnt = 0;
int i, j;
i = x;
j = y;
while (j < n)
{
if (a[i][++j] == 1)//记得一定不用把该点判断进去,即要++j而不是j++。否则如果该点是1,那四个方向cnt++。
{
cnt++;
break;
}
}
i = x;
j = y;
while (j>0)
{
if (a[i][--j] == 1)
{
cnt++;
break;
}
}i = x;
j = y;
while (i< n)
{
if (a[++i][j] == 1)
{
cnt++;
break;
}
}i = x;
j = y;
while (i> 0)
{
if (a[--i][j] == 1)
{
cnt++;
break;
}
}
return cnt;
}
int main()
{
scanf_s("%d", &n);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
scanf_s("%d", &a[i][j]);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (f(i, j)==4&&a[i][j]!=1)//f()==4代表该点被包围
{
a[i][j] = 2;
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
printf("%d ", a[i][j]);
printf("\n");
}
return 0;
}
后来在洛谷上只通过了5个例,其实这个代码有很大缺陷,比如当1围成G的形状时,也会算成被包围,显然是错误的。
应该有优化的可能性,不过暂时想不出了,所以还是老老实实用DFS吧。
思路很简单,先把不是1的都赋值2,再通过对第一个点的DFS,将包围圈外面的赋值0;
DFS也要注意,如果只是从地图的第一个点开始DFS,如果第一个点就是1,可能就会把被包围的当成包围圈外面的。所以要在地图外面加一圈。
//涂色
//首先在所给地图的外面加上一圈,可以避免一些错误,比如1在地图边界处
#include<stdio.h>
struct note {
int x;
int y;
};
int n,tx,ty, a[35][35], book[35][35];
int main()
{
struct note que[1000];
int n, a[35][35];
int next[4][2] = { {0,1},{1,0},{0,-1},{-1,0} };
scanf("%d", &n);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
scanf("%d", &a[i][j]);
if(a[i][j]==0)
a[i][j] = 2;//先把大地图不是1的地方赋值2;后面用dfs把在包围外面的变成0即可
}
for (int i = 0; i <= n + 1; i++)
{
a[0][i] = 2;
a[n + 1][i] = 2;
}
for (int i = 0; i <= n + 1; i++)
{
a[i][0] = 2;
a[i][n + 1] = 2;
}
int head, tail;
head = 0;
tail = 0;//0还是1?
que[tail].x = 0;
que[tail].y = 0;
book[0][0] = 1;
tail++;
while (head < tail)//队列不为空时循环
{
for (int k = 0; k <= 3; k++)
{
tx = que[head].x + next[k][0];
ty = que[head].y + next[k][1];
if (tx<0 || tx>n+1 || ty<0 || ty>n+1)
continue;
if (a[tx][ty] == 2 && book[tx][ty] == 0)
{
book[tx][ty] = 1;
a[tx][ty] = 0;
que[tail].x = tx;
que[tail].y = ty;
tail++;
}
}
head++;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
printf("%d ", a[i][j]);
printf("\n");
}
return 0;
}
Fooldfill漫水填充法//需要使用深度优先搜索
#include<stdio.h>
int n, m, a[25][25],book[25][25];
void dfs(int x, int y, int color)
{
int next[4][2] = { {0,1},{1,0},{0,-1},{-1,0} };//四个方向
int tx, ty;
for (int k = 0; k <= 3; k++)
{
tx = x + next[k][0];
ty = y + next[k][1];
if (tx<1 || tx>n || ty<1 || ty>m)
continue;
if (a[tx][ty] > 0&&book[tx][ty] == 0)
{
a[tx][ty] = color;
book[tx][ty] = 1;
dfs(tx, ty, color);
}
}
return;
}
int main()
{
int num = 0;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);
//对每个大于0的点尝试进行dfs染色
for(int i=1;i<=n;i++)
for (int j = 1; j <= m; j++)
{
if (a[i][j] > 0)
{
num--;
book[i][j] = 1;//容易忽略
a[i][j] = num;//对每片区域的第一个数也要染色
dfs(i, j, num);
}
}
//输出染色后的地图
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
printf("%3d", a[i][j]);
printf("\n");
}
printf("有%d个小岛", -num);
return 0;
}
图的遍历
深度优先搜索遍历邻接矩阵
输入邻接矩阵的长和宽,再告诉你图中顶点的边,要求输出图,(输出按照被深度优先搜索遍历到的顺序)
//需要我们做以下几件事
1 将图以邻接矩阵的形式储存下来,顶点1和顶点2有边,就是e[1][2]=1,因为是无向图,所以还有e[2][1]=1。没有边就是e[2][1]=99999999,顶点与自身是e[2][2]=0;
2 定义一个深搜函数dfs,参数只有一个,表示当前遍历到的顶点,函数递归结束的条件是sum==n,其中sum表示目前遍历完的顶点的个数,n是顶点个数。如果没有达到结束条件, 遍历邻接矩阵,判断当前顶点与顶点i是否有边,顶点i是否已经访问过
#include<stdio.h>
int book[101], sum, n, e[101][101];
void dfs(int cur)
{
printf("%d ", cur);//遍历到哪个顶点就输出哪个顶点
//遍历完一个顶点就sum++
sum++;
if (sum == n) return;
//如果顶点还没有被遍历完
for (int i = 1; i <= n; i++)
{
if (e[cur][i] == 1 && book[i] == 0)
{
book[i] = 1;
dfs(i);
}
}
return;
}
int main()
{
int m, a, b;
scanf_s("%d %d", &n, &m);//这里n表示的是正方形邻接矩阵的边长,m表示的是输入的顶点的边的数量,m=3就输入3对,m=5就输入5对
for(int i=1;i<=n;i++)
for (int j = 1; j <= n; j++)
{
if (i == j)e[i][j] = 0;
e[i][j] = 99999999;
}
for (int i = 1; i <= m; i++)
{
scanf_s("%d %d", &a, &b);
e[a][b] = 1;
e[b][a] = 1;
}
book[1] = 1;
dfs(1);
return 0;
}
问题 H: 密码锁问题(JSU-ZJJ内存限制:64 MB时间限制:5.000 S
内存限制:64 MB时间限制:5.000 S
评测方式:文本比较命题人:外部导入
提交:365解决:279
题目描述
一个微调密码锁是这样的一种锁,这种锁你仅能转动密码盘。这是一种常见的密码盘,通过仅在允许的组中改变这些密码盘以微调某个值。
设想一行有D个编号的密码盘,每个密码盘顺序有0到9共九个数字。这类似于密码箱的组合锁。
下面是一系列B按钮,每个按钮标记有D位数字。例如,D可能是4标记就是1000 1200 1002 0111.按标记为1000的按钮,则仅转动第一个转盘一次,而其他转盘不动,而按按钮1002则转第一个转盘一次,转第四个转盘两次,剩下的不动。每个盘按循环的方式转动,即如果转到9,再转一次,就又转回0.
你的任务是模仿这样一个上锁的微调密码锁,给出最终的各密码盘的读数。输入
输入的每个测试数据的第一行包含有D个数字(至多10个),表示密码盘的起始位置。接下来的每一行有一排有标记的按钮,表示下一次会按的按钮。
输出
对每个测试用例用一行输出最终各密码盘的读数。
样例输入 复制
0001 1003 0206 0034 1111 1003
样例输出 复制
3348
思路其实很简单,先通过第一行数据得出密码的长度并将该串字符的每一位的值-‘0’(字符‘6’的阿斯伽马值减去’0‘的阿斯伽马值得到数字6),然后多组输入,每输入一串字符,就将该串字符的每一位的值-‘0’(字符‘6’的阿斯伽马值减去’0‘的阿斯伽马值得到数字6),累加到对应的c[i]里,最后输出的时候用a[i]%10就可以了。
#include<stdio.h>
#include<string.h>
int c[15];
int main()
{
int i = 0,n;
char a[100];
char b[100];
gets(a);
n = strlen(a);
for (int i = 0; i < n; i++)
c[i] += a[i]-'0';//字符‘6’的阿斯伽马值减去’0‘的阿斯伽马值得到数字6
while (gets(b) != NULL)
{
for (int i = 0; i < n; i++)
c[i] += b[i]-'0';
}
for (int i = 0; i < n; i++)
printf("%d", c[i] % 10);//一共10个数字,对10取余即可
return 0;
}
问题 F: 数数字
内存限制:128 MB时间限制:1.000 S
评测方式:文本比较命题人:20154206121
提交:4385解决:1185
题目描述
问题很简单有个1到n的数列,数一下其中能够被2,3,5整除的数字的个数。例如当n = 6 ,的时候有 2,3,4,5 , 6.这5个数满足条件,所以我们应该输出5,是不是很简单?
输入
多组输入到文件尾,每组输入一个n (n < 1e9 )
输出
输出对应的个数
样例输入 复制
1 2 6
样例输出 复制
0 1 5
一开始没注意时间限制,写上去一提交发现事情不对劲,然后想用数组把能被整除的记录下来,后来发现这样很蠢,一是这样并不能节约时间,二是没有这么大的数组。
后来在csdn上看到另外一个题目的题解,说的是n/6的值就是从1到n内能被6整除的数的个数。比如100/3=33.3,那么从1到100内就有33个数能被3整除。后来想想其实特别好理解,就是100能被3分成33.3份,我从3开始,每次加3,能加32次,加上一开始的3就是33个数,每次加的都是3肯定能被3整除。
但是这还不够,因为1到6满足条件的有3+2+1=6种,但答案却是5,其实是因为6既能被2整除又能被3整除。于是在百度上发现了下面这个容斥定理:
A类和B类和C类元素个数总和= A类元素个数+ B类元素个数+C类元素个数—既是A类又是B类的元素个数—既是A类又是C类的元素个数—既是B类又是C类的元素个数+既是A类又是B类而且是C类的元素个数。(A∪B∪C = A+B+C - A∩B - B∩C - C∩A + A∩B∩C)
这时事情就变得简单起来了,代码如下:
#include<stdio.h>
int main()
{
int n;
int cnt = 0;
while (~scanf("%d", &n))
{
cnt = n / 2 + n / 5 + n /3;
cnt = cnt - n / 6 - n / 10 - n / 15 + n / 30;
//n/6是既能被2整除又能被3整除的
//n/10是既能被2整除又能被5整除的
//n/15是既能被3整除又能被5整除的
//n/30是既能被2整除又能被3整除还能被5整除的
printf("%d", cnt);
}
return 0;
}