题目链接: POJ 2870 Light Up
分析:
求最小步数,首先想到了IDA*,但是写了1个多小时,严重超时。仔细思考后发现,步数可以达到20步以上,比如以下这个样例需要25步:
7 7
24
1 2 -1
1 4 -1
1 6 -1
2 1 -1
2 3 -1
2 5 -1
2 7 -1
3 2 -1
3 4 -1
3 6 -1
4 1 -1
4 3 -1
4 5 -1
4 7 -1
5 2 -1
5 4 -1
5 6 -1
6 1 -1
6 3 -1
6 5 -1
6 7 -1
7 2 -1
7 4 -1
7 6 -1
所以在用IDA*之前,还是要想一想步数会不会太多。
谈一谈朴素的深搜——
地图类型的搜索(例如数独) 一般都是采用一格一格的搜索,有时可以改变顺序(例如数独很多时候需要从条件最多的格子入手)。
如果按顺序搜索会有一个剪枝的方便之处,本题就用到了——后面的行极有可能无法影响前面的格子。
本题代码如下:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define xx first
#define yy second
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
#define INTpair pair<int,int>
using namespace std;
const int maxn=20,inf=1e9;
int n,m,B,s[maxn][maxn],lit[maxn][maxn];
bool lamp[maxn][maxn];
int ans;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
vector<INTpair> v;
void Change(int x,int y,int type){
//点亮(type==1)或者 熄灭(type==-1)这个点
int i;
lamp[x][y]= (type==1);
for(i=y;i>0&&s[x][i]==-2;i--)lit[x][i]+=type;
for(i=y+1;i<=m&&s[x][i]==-2;i++)lit[x][i]+=type;
for(i=x-1;i>0&&s[i][y]==-2;i--)lit[i][y]+=type;
for(i=x+1;i<=n&&s[i][y]==-2;i++)lit[i][y]+=type;
}
bool check1(){ //检查带编号的墙 ,是否满足要求
int i,j;
for(i=0;i<v.size();i++){
int x=v[i].xx,y=v[i].yy,cnt=0;
for(j=0;j<4;j++)
if(lamp[x+dx[j]][y+dy[j]])cnt++;
if(cnt!=s[x][y]) return false;
}
return true;
}
bool check2(){
//剪枝1: 检查带编号的墙 ,是否已经有超过要求的,有返回false
int i,j;
for(i=0;i<v.size();i++){
int x=v[i].xx,y=v[i].yy,cnt=0;
for(j=0;j<4;j++)
if(lamp[x+dx[j]][y+dy[j]])cnt++;
if(cnt>s[x][y]) return false;
}
return true;
}
bool check3(int x,int y){
//检查(x,y)这个点会不会和已经有的灯冲突
int i;
for(i=y-1;i>0&&s[x][i]==-2;i--)if(lamp[x][i])return false;
for(i=x-1;i>0&&s[i][y]==-2;i--)if(lamp[i][y])return false;
return true;
}
bool ALL(){ //检查是否全部点亮
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(s[i][j]==-2&&!lit[i][j])return false;
return true;
}
bool Above(int x,int y){
/* 强剪枝:
检查墙(x,y) ,如果他的正上方还有没点亮的,那之后也没法点亮
*/
for(int i=1;i<x;i++)
if(s[i][y]==-2&&!lit[i][y]) return true;
return false;
}
void PrintMap(){
cout<<"*********&&&&&&&&&&************"<<endl;
for(int i=1;i<=n;i++,cout<<endl)
for(int j=1;j<=m;j++)cout<<lamp[i][j]<<" ";
cout<<"*********&&&&&&&&&&************"<<endl;
}
void DFS(int x,int y,int dep){ //当前讨论(x,y)这个点,已经放了dep个灯
//cout<<x<<" "<<y<<" "<<dep<<endl;
if(dep>=ans) return ;
if(!check2()) return ;
if(y==1&&x==n+1&&check1()&&ALL()){
ans=min(ans,dep);
//PrintMap();
return ;
}
if(x>n||y>m) return ;
if(s[x][y]>=-1){ //搜到墙了
if(x>1&&s[x-1][y]==-2&& Above(x,y))return;
if(y<m) DFS(x,y+1,dep);
else DFS(x+1,1,dep);
}
else {
if(y<m)DFS(x,y+1,dep);
else DFS(x+1,1,dep);
if(check3(x,y)){
Change(x,y,1);
if(y<m)DFS(x,y+1,dep+1);
else DFS(x+1,1,dep+1);
Change(x,y,-1);
}
}
}
int main(){
int i,j,x,y;
while(cin>>n>>m){
if(!n&&!m) return 0;
CLEAR(s);CLEAR(lit); CLEAR(lamp);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)s[i][j]=-2;
cin>>B;
v.clear();
for(i=1;i<=B;i++){
cin>>x>>y;
cin>>s[x][y];
if(s[x][y]!=-1)v.push_back(make_pair(x,y));
}
ans=inf;
DFS(1,1,0);
if(ans==inf)cout<<"No solution"<<endl;
else cout<<ans<<endl;
}
return 0;
}