题目链接: 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; }