文章目錄
零碎知識整理
- 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;
}