2013杭州区域赛的题目
题意:
给你一张图,所以空的地方必须点亮,非空的地方不能点亮。灯能放在空的地方,固定会照耀(x,y) (x-1,y) (x,y+1)三点。然后你还有一盏特殊的灯,能旋转。最多15个空位。求最少的灯覆盖所有的空位。
思路:
状态压缩,枚举灯在15个位置的放置情况,如果不合法再枚举旋转哪盏灯到哪个方向。
可以记录点亮的次数A和点亮在空的地方的次数B。如果A!=B说明有点亮非空的位置,那么是不合法的。这样判断快一些。
复杂度O(15*4*2^15)
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 200010
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD =1000000007;
const double EPS=1e-7;
const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, 1, 0, -1};
int n,m,ans,M,light,legal;
char mp[256][256];
int cnt[16],id[256][256];
vector<PI> a;
bool check(){
for(int i=0;i<M;i++) if(cnt[i]==0) return false;
return light==legal;
}
void put(int x,int y,int op,int c){
light+=c;
legal+=c;
cnt[id[x][y]]+=c;
for(int i=op;i!=(op+2)%4;i=(i+1)%4){
int xx=dx[i]+x, yy=dy[i]+y;
if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
light+=c;
if(mp[xx][yy]=='.'){
legal+=c;
cnt[id[xx][yy]]+=c;
}
}
}
int main(){
while(~scanf("%d%d",&n,&m),n||m){
M=0;
a.clear();
for(int i=0;i<n;i++){
scanf("%s",mp[i]);
for(int j=0;j<m;j++) if(mp[i][j]=='.'){
a.push_back(make_pair(i,j));
id[i][j]=M++;
}
}
ans=INF;
for(int st=0;st<(1<<M);st++){
int num=0;
light=legal=0;
CLR(cnt,0);
for(int i=0;i<M;i++) if(1<<i&st){
num++;
put(a[i].first,a[i].second,0,1);
}
if(num>=ans) continue;
if(check()) ans=min(ans,num);
else{
for(int i=0;i<M;i++) if(1<<i&st){
int x=a[i].first, y=a[i].second;
put(x,y,0,-1);
for(int j=1;j<4;j++){
put(x,y,j,1);
if(check()) ans=min(ans,num);
put(x,y,j,-1);
}
put(x,y,0,1);
}
}
}
if(ans==INF) ans=-1;
printf("%d\n",ans);
}
return 0;
}