文章目录
零碎知识整理
- next_permutation是STL中的函数。从字典序最小排列开始,求字典序下一个排列。
sort(a, a+n);
do{
}while(next_permutation(a, a+n);
- memcmp(a, b, sizeof(b)),比较b和a的大小。返回值小于0时,表示a<b;返回值大于0时,表示a>b,返回值等于0时,表示a==b。
- memcpy(a, b, sizeof(b)),将b赋值给a。
题目
725 - Division
题目链接:725 - Division
- 题目大意:输入正整数n,按从小到大的顺序输出所有形如abcde/fghij=n的表达式,其中a—j恰好为数字0—9的一个排列(可以有0前导) 2=<n<=79。
- 思路:暴力枚举。枚举分子a范围为[1234,99999/n],则分母b为a*n,然后判断组成a和b的数字有没有重复。
代码:
#include <iostream>
#include <cstdio>
#include <set>
using namespace std;
//判断a和b的数字是否充分
bool judge(int a, int b)
{
set<int> Set;
int t;
if(a<10000) Set.insert(0);
while(a)
{
t = a%10;
a /= 10;
Set.insert(t);
}
if(Set.size()<5) return 0;
while(b)
{
t = b%10;
b /= 10;
Set.insert(t);
}
if(Set.size()<10) return 0;
else return 1;
}
int main()
{
int N, kase = 0;
while(cin >> N && N)
{
if(kase++) printf("\n");
int flag = 0;
for(int j=1234; j<=99999/N; j++)
{
if(judge(j, j*N))
{
flag = 1;
if(j<10000)
printf("%d / 0%d = %d\n", j*N, j, N);
else
printf("%d / %d = %d\n", j*N, j, N);
}
}
if(!flag) printf("There are no solutions for %d.\n", N);
}
return 0;
}
11059 - Maximum Product
- 题目大意:输入n个元素组成的序列S,你需要找出一个乘积最大的连续子序列。如果这个最大的乘积不是正数,应输出0(表示无解)。1≤n≤18,-10≤Si≤10。
样例输入:
3
2 4-3
5
2 5 -1 2 -1
样例输出:
8
20 - 思路:暴力枚举。枚举连续序列的起点和终点,结果是long long 类型。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
int main()
{
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout);
int N, a[20], kase = 1;
while(cin >> N)
{
LL Max = 0;
for(int i=0; i<N; i++) cin >> a[i];
for(int i=0; i<N; i++)
{
for(int j=i; j<N; j++)
{
LL ans = 1;
for(int k=i; k<=j; k++)//注意是等于号
{
ans = ans*a[k];
}
if(Max<ans) Max = ans;
}
}
printf("Case #%d: The maximum product is %lld.\n\n", kase++, Max);
}
return 0;
}
10976 - Fractions Again?!
题目链接:10976 - Fractions Again?!
- 题目大意:给你一个数k,找到所有的正整数x>=y,使得1/k = 1/x + 1/y成立 。
- 思路:暴力求解,但是因为x和y的取值范围太大,所以需要缩小其枚举范围。
推导:x>=y => 1/x<=1/y,所以1/k-1/y<=1/y,所以y<=2k;然后1/k=1/x+1/y => x=(ky)/(y-k)。所以从[k+1, 2k]范围内枚举y使得k*y除以y-k的值为整数即可得到x值。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int main()
{
int N, X[10000], Y[10000];
while(cin >> N)
{
int cnt = 0;
for(int y=N+1; y<=2*N; y++)
{
if((N*y)%(y-N)==0)//x为整数
{
X[cnt] = (N*y)/(y-N);
Y[cnt++] = y;
}
}
printf("%d\n", cnt);
for(int i=0; i<cnt; i++)
printf("1/%d = 1/%d + 1/%d\n", N, X[i], Y[i]);
}
return 0;
}
524 - Prime Ring Problem
- 题目大意:输入正整数n,把1—n组成一个环,是相邻的两个整数为素数。输出时从整数1开始,逆时针排列。同一个环恰好输出一次,n (0 < n <= 16)
- 思路:回溯法+dfs。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int n, vis[20];
vector<int> v;
//判断是否是素数
bool isPrime(int u)
{
for(int i=2; i*i<=u; i++)
{
if(u%i==0) return 0;
}
return 1;
}
//dfs回溯求出所有满足条件的素数环
void dfs()
{
if(v.size()==n && isPrime(v[0]+v[n-1]))
{
for(int i=0; i<n; i++)
{
if(i) printf(" ");
printf("%d", v[i]);
}
printf("\n");
}
else
{
for(int i=2; i<=n; i++)
{
if(!vis[i] && isPrime(i+v[v.size()-1]))
{
vis[i] = 1;
v.push_back(i);
dfs();
v.pop_back();
vis[i] = 0;
}
}
}
}
int main()
{
int kase = 1;
while(cin >> n)
{
//初始化
v.clear();
memset(vis, 0, sizeof(vis));
vis[1] = 1;
v.push_back(1);
if(kase>1) printf("\n");
printf("Case %d:\n", kase++);
dfs();
}
return 0;
}
129 - Krypton Factor
题目链接:129 - Krypton Factor
- 题目大意:一个字母串里包含有两个相邻的重复子串则称为“水串”,否则为“火串”
例如AA、ABCABC都是"水串",而D、DC、ABDAD、CBABCBA都是“火串"。
输入正整数L和n,输出由前L个字母组成的、字典序第n个的"火串"。 - 思路:dfs+回溯。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
vector<char> v;
int L, n, Rank;//Rank表示该串的排名
//判断该串是否是重串
int check(vector<char> v)
{
int len = v.size();
//i表示判断重复串的长度,j表示串字符的下标
for(int i=1; i<=len/2; i++)
{
int j;
for(j=len-i*2; j<len-i; j++)
{
if(v[j]!=v[j+i]) break;//表明两个串并不重复
}
if(j==len-i) return 0;//两个串重复
}
return 1;
}
bool dfs()
{
if(Rank==L) return 1;//表明得到了该串
//DFS遍历
for(int i=0; i<n; i++)
{
v.push_back(i+'A');
if(!check(v)) v.pop_back();
else
{
Rank++;
if(dfs()) return 1;
v.pop_back();
}
}
return 0;//回溯
}
int main()
{
while(cin >> L >> n && (L+n))
{
v.clear();
Rank = 0;
dfs();
//格式打印输出
int cnt = 0;
for(int i=0; i<v.size(); i++)
{
if(i && cnt%64==0) printf("\n");
else if(i && cnt%4==0) printf(" ");
printf("%c", v[i]);
cnt++;
}
printf("\n");
printf("%d\n", v.size());
}
return 0;
}
140 - Bandwidth
题目链接:140 - Bandwidth
- 题目大意:给出一个n个节点的图,求该图中相邻的节点在哪一个由这些节点组成的排列中,带宽最小,输出该排列和最小带宽。(各个节点到相邻节点的最远距离,这些最远距离的最大值即为带宽)。
- 思路:因为结点数目较少,所以可以使用nextpermutation来枚举各个排列,然后再减枝筛选。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <cmath>
#include <algorithm>
using namespace std;
const int INF = 1<<29;
string s;
set<int> G[30];//存储相关联元素
set<int> S;//存储所有元素,不重复
int Min, ans[30], c[30], n;//ans是结果数组,c是序列数组
void Init()
{
Min = INF;
S.clear();
for(int i=0; i<30; i++) G[i].clear();
}
void solve()
{
do
{
int cnt = 0, flag = 1;//cnt是该序列对应的带宽
for(int i=0; i<n; i++)
{
int t = c[i], num = G[t].size();//t是元素,num是该元素相关联的元素的个数
for(int k=0; k<n; k++)
{
if(G[t].count(c[k]))
{
int d = abs(i-k);
if(d>=Min)//带宽已经超过了之前的
{
flag = 0;
break;
}
if(cnt<d) cnt = d;//更新带宽
num--;//已经计算过一个了
}
if(i==k && num>=Min)//还剩num个相关联元素,且num大于之前的带宽
{
flag = 0;
break;
}
}
if(!flag) break;
}
if(flag && cnt<Min)
{
Min = cnt;
for(int k=0; k<n; k++)
ans[k] = c[k];
}
}while(next_permutation(c, c+n));
}
void Print()
{
for(int i=0; i<n; i++)
printf("%c ", ans[i]+'A');
printf("-> %d\n", Min);
}
int main()
{
while(cin >> s && s!="#")
{
Init();
int flag = 0, a, b;
for(int i=0; i<s.size(); i++)
{
if(s[i]==':') flag = 1;
else if(s[i]==';') flag = 0;
else if(!flag)
{
a = s[i]-'A';
S.insert(a);
}
else if(flag)
{
b = s[i]-'A';
G[a].insert(b);
G[b].insert(a);
S.insert(b);
}
}
n = S.size();
set<int>::iterator it = S.begin();
for(int i=0; i<n; i++)
{
c[i] = *it;
it++;
}
solve();
Print();
}
return 0;
}
1354 - Mobile Computing
题目链接:1354 - Mobile Computing</a
- 题目大意:给出房间的宽度r和s个挂坠的重量wi,设计一个尽量宽但小于房间宽度的天平。
如果要使两个重量不同的w1,w2坠在天平两边,就要满足如下等式w1到中点距离为l1=(w2)/(w1+w2),w2到中点距离为l2=(w1)/(w1+w2);
因为一个天平两臂长度为1(l1+l2=1),而且根据物理学公式可知w1*(l1)=w2*(l2); - 思路:
10603 - Fill
题目链接:10603 - Fill
- 题目大意:三个杯子容量分别为a,b,c,前两个是空的,第三个装满水,目标是量出d升水。
- 思路:隐式图转换。状态是三个杯子的水量和已经倒的水量。采用bfs遍历,得到目标态,遍历是将一个杯子中的水倒到另一个杯子(杯子水倒完或杯子倒满)。同时还有去重数组,即vis[][]下标表示前两个杯子的容量,来标记状态是否已经访问过。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
struct Node
{
int v[3], dist;
bool operator < (const Node &A) const
{
return dist>A.dist;
}
};
const int maxn = 205;
int vis[maxn][maxn], cap[3], ans[maxn];
void update_ans(const Node &u)
{
for(int i=0; i<3; i++)
{
int d = u.v[i];
if(ans[d]<0 || u.dist<ans[d]) ans[d] = u.dist;//表示当倒ans[d]水量时,可以得到d升水
}
}
void solve(int a, int b, int c, int d)
{
cap[0] = a, cap[1] = b, cap[2] = c;
memset(vis, 0, sizeof(vis));
memset(ans, -1, sizeof(ans));
priority_queue<Node> q;
Node start;
start.dist = 0;
start.v[0] = 0, start.v[1] = 0, start.v[2] = c;
q.push(start);
vis[0][0] = 1;//初始状态
while(!q.empty())
{
Node u = q.top(); q.pop();
update_ans(u);
if(ans[d]>=0) break;
for(int i=0; i<3; i++)//从i向j里面倒水
{
for(int j=0; j<3; j++)
{
if(i!=j)
{
if(u.v[i]==0 || u.v[j]==cap[j]) continue;
int amount = min(cap[j], u.v[i]+u.v[j])-u.v[j];//倒的水量
Node u2;
memcpy(&u2, &u, sizeof(u));
u2.dist = u.dist + amount;//结果水量
u2.v[i] -= amount;
u2.v[j] += amount;
if(!vis[u2.v[0]][u2.v[1]])//该状态没有出现过
{
vis[u2.v[0]][u2.v[1]] = 1;
q.push(u2);
}
}
}
}
}
while(d>=0)//输出与d最接近的
{
if(ans[d]>0)
{
printf("%d %d\n", ans[d], d);
return;
}
d--;
}
}
int main()
{
int T, a, b, c, d;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d%d", &a, &b, &c, &d);
solve(a, b, c, d);
}
return 0;
}
208 - Firetruck
题目链接:208 - Firetruck
- 题目大意:一个无向图,从一个点到另一个点,按序输出其路径。
- 思路:细节是在递归回溯之前需要判断是否存在路径。使用dfs判断是否有路径,然后递归回溯,当到达目标点时输出路径。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 50;
const int INF = 1<<29;
int G[maxn][maxn], n, sum;
int vis[maxn], Max, ans[maxn];
void Init()
{
sum = 0;
for(int i=0; i<maxn; i++)
{
for(int j=0; j<maxn; j++)
G[i][j] = INF;
}
memset(vis, 0, sizeof(vis));
}
bool dfs(int st)
{
if(st==n) return 1;
vis[st] = 1;
for(int i=1; i<=Max; i++)
{
if(!vis[i] && G[st][i]!=INF)
{
if(dfs(i)) return 1;
}
}
return 0;
}
void solve(int st, int cur)
{
if(st==n)
{
sum++;//路径数目
for(int i=0; i<cur; i++)
{
if(i) printf(" ");
printf("%d", ans[i]);
}
printf("\n");
}
else
{
for(int i=1; i<=Max; i++)
{
if(vis[i]) continue;
if(G[st][i]!=INF)
{
ans[cur] = i;
vis[i] = 1;
solve(i, cur+1);
vis[i] = 0;
}
}
}
}
int main()
{
int a, b, kase = 1;
while(cin >> n)
{
Init();
Max = 0;
while(cin >> a >> b && (a+b))
{
G[a][b] = 1;
G[b][a] = 1;
if(Max<a) Max = a;
if(Max<b) Max = b;
}
printf("CASE %d:\n", kase++);
if(!dfs(1))
{
printf("There are %d routes from the firestation to streetcorner %d.\n", 0, n);
continue;
}
memset(vis, 0, sizeof(vis));
ans[0] = 1;
vis[1] = 1;
solve(1, 1);
printf("There are %d routes from the firestation to streetcorner %d.\n", sum, n);
}
return 0;
}
1601 - The Morning after Halloween
题目链接:1601 - The Morning after Halloween
- 题目大意:在一张图中,以最少的步数将a,b,c移到对应的A,B,C上去。其中,每个2x2的方格都有障碍并且不能两个小写字母同时占据一个格子。
- 思路:因为障碍格较多,可以将一个位置的相邻非障碍个存储为关于该位置的一个数组,则需要将二维的位置转换为一维的下标,来降低空间复杂度,然后使用二维数组存储该位置的非障碍相邻位置。将三个点作为状态(不够则补),之后就是可以BFS搜索。
单BFS
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxl = 20;
const int maxn = 150;
int w, h, n;
int deg[maxn];
int G[maxn][5];
int s[3], t[3];
int d[maxn][maxn][maxn];
int x[maxn], y[maxn], id[maxl][maxl];
char maze[maxl][maxl];
int dx[] = {0, 0, 0, -1, 1};
int dy[] = {0, 1, -1, 0, 0};
int ID(int a, int b, int c){
return (a<<16) | (b<<8) | c;
}
bool conflict(int a, int b, int a2, int b2){
return a2 == b2 || (a2 == b && b2 == a);
}
int BFS(){
memset(d, -1, sizeof(d));//d存储到达该状态的步数
queue<int>q;
q.push(ID(s[0], s[1], s[2]));//三个鬼的位置作为状态
d[s[0]][s[1]][s[2]] = 0;
while(!q.empty()){
int u = q.front(); q.pop();
int a = (u>>16) & 0xff, b = (u>>8) & 0xff, c = u & 0xff;
if(a == t[0] && b == t[1] && c == t[2]) return d[a][b][c];//到达目标状态
for(int i = 0; i < deg[a]; i++){//a移动
int a2 = G[a][i];
for(int j = 0; j < deg[b]; j++){//b移动
int b2 = G[b][j];
if(conflict(a, b, a2, b2)) continue;//a和b冲突
for(int k = 0; k < deg[c]; k++){//c移动
int c2 = G[c][k];
if(conflict(a, c, a2, c2)) continue;
if(conflict(b, c, b2, c2)) continue;
if(d[a2][b2][c2] != -1) continue;//如果已经遍历过该状态
q.push(ID(a2, b2, c2));
d[a2][b2][c2] = d[a][b][c] + 1;
}
}
}
}
return -1;
}
int main()
{
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
while(scanf("%d%d%d", &w, &h, &n) == 3 && w){
getchar();
for(int i = 0; i < h; i++)
fgets(maze[i], maxl, stdin);
//将非墙的二维坐标转换为一维下标
int cnt = 0;
for(int i = 0; i < h; i++){
for(int j = 0; j < w; j++){
if(maze[i][j] != '#'){
x[cnt] = i, y[cnt] = j, id[i][j] = cnt;//x,y分别为该下标对应的二维坐标,id是该二维坐标对应的下标
if(islower(maze[i][j])) s[maze[i][j] - 'a'] = cnt;//出发点的下标
if(isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;//目标点的下标
cnt++;
}
}
}
for(int i = 0; i < cnt; i++){
deg[i] = 0;//表示下标为i的位置相邻的非墙的位置的个数
for(int j = 0; j < 5; j++){
int nx = x[i] + dx[j], ny = y[i] + dy[j];
if(maze[nx][ny] != '#'){ //题目中说了迷宫的最外面是墙 因此不用判断边界
G[i][deg[i]++] = id[nx][ny];//G[i]表示下标为i的位置相邻的位置
}
}
}
//如果鬼的数量小于3个
if(n <= 2){ deg[cnt] = 1; G[cnt][0] = cnt; s[2] = t[2] = cnt++; }
if(n <= 1){ deg[cnt] = 1; G[cnt][0] = cnt; s[1] = t[1] = cnt++; }
printf("%d\n", BFS());
}
return 0;
}
双向BFS
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxl = 20;
const int maxn = 150;
int w, h, n;
int deg[maxn];
int G[maxn][5];
int s[3], t[3];
int d[2][maxn][maxn][maxn];
int x[maxn], y[maxn], id[maxl][maxl];
int mark = 0;
char maze[maxl][maxl];
int dx[] = {0, 0, 0, -1, 1};
int dy[] = {0, 1, -1, 0, 0};
int ID(int a, int b, int c){
return (a<<16) | (b<<8) | c;
}
bool conflict(int a, int b, int a2, int b2){
return a2 == b2 || (a2 == b && b2 == a);
}
void expand(queue<int>& q, int n){
int u = q.front(); q.pop();
int a = (u>>16) & 0xff, b = (u>>8) & 0xff, c = u & 0xff;
if(d[1-n][a][b][c] != -1) mark = d[1-n][a][b][c] + d[n][a][b][c];//表明另一个队列到过同一个地方,则mark值即为答案
for(int i = 0; i < deg[a]; i++){
int a2 = G[a][i];
for(int j = 0; j < deg[b]; j++){
int b2 = G[b][j];
if(conflict(a, b, a2, b2)) continue;
for(int k = 0; k < deg[c]; k++){
int c2 = G[c][k];
if(conflict(a, c, a2, c2)) continue;
if(conflict(b, c, b2, c2)) continue;
if(d[n][a2][b2][c2] != -1) continue;
int t = ID(a2, b2, c2);
q.push(t);
d[n][a2][b2][c2] = d[n][a][b][c] + 1;
}
}
}
}
int DBFS(){
memset(d, -1, sizeof(d));
queue<int>q[2];
q[0].push(ID(s[0], s[1], s[2]));
d[0][s[0]][s[1]][s[2]] = 0;
q[1].push(ID(t[0], t[1], t[2]));
d[1][t[0]][t[1]][t[2]] = 0;
int order = 0;
mark = 0;
while(!q[0].empty() && !q[1].empty()){
order ? expand(q[0], 0) : expand(q[1], 1);//两个队列交替进行
if(mark) return mark;
order = !order;
}
return -1;
}
int main()
{
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
while(scanf("%d%d%d", &w, &h, &n) == 3 && w){
getchar();
for(int i = 0; i < h; i++)
fgets(maze[i], maxl, stdin);
//将非墙的二维坐标转换为一维下标
int cnt = 0;
for(int i = 0; i < h; i++){
for(int j = 0; j < w; j++){
if(maze[i][j] != '#'){
x[cnt] = i, y[cnt] = j, id[i][j] = cnt;//x,y分别为该下标对应的二维坐标,id是该二维坐标对应的下标
if(islower(maze[i][j])) s[maze[i][j] - 'a'] = cnt;//出发点的下标
if(isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;//目标点的下标
cnt++;
}
}
}
for(int i = 0; i < cnt; i++){
deg[i] = 0;//表示下标为i的位置相邻的非墙的位置的个数
for(int j = 0; j < 5; j++){
int nx = x[i] + dx[j], ny = y[i] + dy[j];
if(maze[nx][ny] != '#'){ //题目中说了迷宫的最外面是墙 因此不用判断边界
G[i][deg[i]++] = id[nx][ny];//G[i]表示下标为i的位置相邻的位置
}
}
}
//如果鬼的数量小于3个
if(n <= 2){ deg[cnt] = 1; G[cnt][0] = cnt; s[2] = t[2] = cnt++; }
if(n <= 1){ deg[cnt] = 1; G[cnt][0] = cnt; s[1] = t[1] = cnt++; }
printf("%d\n", DBFS());
}
return 0;
}
12325 - Zombie’s Treasure Chest
题目链接:12325 - Zombie’s Treasure Chest
- 题目大意:一个箱子,体积为N
两种宝物,体积为S1、S2,价值为V1、V2,数量无限
最多装多少价值的宝物
数据范围:2^32 - 思路:数据范围比较大,无法使用动态规划,使用枚举,但是需要缩小枚举范围。首先可以枚举1的数量范围为N/S1,但当S1较小时,范围还是较大,宝物2同理。但是若存在V1S2>V2S1,则表明宝物1的性价比高于宝物2,枚举2使得宝物1的数量尽可能多,2的枚举量不超过S1,所以为min(N/S2, S1-1),当大于时同理。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif
LL T, N, S[3], V[3], n, kase = 1, idx;
cin >> T;
while(T--)
{
cin >> N >> S[0] >> V[0] >> S[1] >> V[1];
if(S[0]*V[1]>S[1]*V[0])//V[0]的性价比低,则枚举0,尽可能拿1
n = min(N/S[0], S[1]-1), idx = 0;
else
n = min(N/S[1], S[0]-1), idx = 1;
LL value = 0, num, v;
for(int i=0; i<=n; i++)
{
v = 0, num = N;
num -= S[idx]*i;
v += V[idx]*i;
v += num/S[1-idx]*V[1-idx];
if(v>value) value = v;
}
printf("Case #%lld: %lld\n", kase++, value);
}
return 0;
}
uva - 11214
- 题目大意:一个n*m的棋盘,某些格子有标记。用最少的皇后守卫所有带标记的格子。
- 思路:可以枚举或二分枚举皇后的数目,然后使用dfs来判断是否可以将所有有标记的格子看住。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 15;
char G[maxn][maxn];
vector<int> X;
int vis[5][maxn*10];
int n, m;
void Init()
{
X.clear();
}
//判断是否将所有带标记的格子看住
bool check()
{
for(int i=0; i<X.size(); i++)
{
int x = X[i]/m, y = X[i]%m;
if(!vis[0][x] && !vis[1][y] && !vis[2][x+y] && !vis[3][x-y+m]) return 0;
}
return 1;
}
bool dfs(int num, int cur, int mid)
{
if(num==mid)//所有皇后均放着
{
if(check()) return 1;
return 0;
}
else
{
for(int i=cur; i<n*m; i++)
{
int x = i/m, y = i%m;//坐标
int temp1 = vis[0][x], temp2 = vis[1][y], temp3 = vis[2][x+y], temp4 = vis[3][x-y+m];
if(temp1==1 && temp2==1 && temp3==1 && temp4==1) continue;//减枝,当所有方向均被看住
vis[0][x] = 1, vis[1][y] = 1, vis[2][x+y] = 1, vis[3][x-y+m] = 1;
if(dfs(num+1, i, mid)) return 1;
vis[0][x] = temp1, vis[1][y] = temp2, vis[2][x+y] = temp3, vis[3][x-y+m] = temp4;
}
}
return 0;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif
int kase = 1;
while(scanf("%d", &n) && n)
{
Init();
scanf("%d", &m);
getchar();
for(int i=0; i<n; i++)
{
fgets(G[i], 20, stdin);
}
for(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
{
if(G[i][j]=='X')
X.push_back(i*m+j);//存储带标记的格子的位置
}
}
int l = -1, r = 8, mid;
/*
for(r=0; ; r++)//枚举
{
memset(vis, 0, sizeof(vis));
if(dfs(0, 0, r)) break;
}*/
while(r>l+1)//二分枚举
{
memset(vis, 0, sizeof(vis));
mid = (l+r)/2;
if(dfs(0, 0, mid)) r = mid;
else l = mid;
}
printf("Case %d: %d\n", kase++, r);
}
return 0;
}