原题链接:http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=2214
比赛描述
MiaoMiao和Bird喜欢一起看电视,他们都有一些近视。
有次他们看电视的时候,Bird的眼镜一不小心掉到地上,这下Bird看到电视画面都变模糊了。确切来说,我们把电视上的像素分成两种,用.表示空白区域,用*表示有颜色区域。
电视的画面为n*m的像素矩阵,对于每一个MiaoMiao看到的有色像素*,Bird都会看成
.*.
***
.*.
模糊的方块会相互重叠,可能会使原本空白区域看起来像有颜色的区域。
现在给出Bird看到的电视画面,请你求出MiaoMiao看到的电视画面中有颜色像素的最小值。
输入
第一行两个正整数n(1 ≤ n ≤ 8)和m(1 ≤ m ≤ 8)
接下来有n行,每一行有m个字符('.'或'*')。
输出
输出一个整数,表示MiaoMiao可能看到的有颜色像素数量的最小值,如果Bird看到的情况不存在,则输出-1。
样例输入
3 3
.*.
***
.*.
样例输出
1
题目来源
MoliH
动态规划。
我没有找到什么高效的解法,复杂度n*4^m(如果m>n,可以把整个电视屏幕转置,变成m*4^n),但是所幸n,m都很小。
首先对于每一行每个点是否有像素点的选择会受到上一行的影响。
int flagarray[]记录每个点上一行对应点情况。
flagarray[i]=0 上一行i处为空
flagarray[i]=1上一行i处看到是像素点并且周围没有真正的像素点
flagarray[i]=2上一行i处看到是像素点并且周围有真正的像素点
flagarray[i]=3上一行i处为真正像素点
当flagarray[i]=1,那么这一行的i处必须为真正的像素点,否则,上一行i处看到的就是空白了。
当flagarray[i]=0,那么这一行的i处必须不为真正的像素点,否则,上一行i处看到的就是像素点了。
当flagarray[i]=2,这一行i出可以有真正的像素点。
当flagarray[i]=3,这一行i处以及周围可以不必在放置真正像素点。
当第i行的flagarray[]已经确定的情况下,那么从第i行到最后一行的所有结果已经是一个确定值了。
不妨定义dp[i][flag],记录在第i行flagarray下,从i到n-1行的真正像素点数量。(flag=flagarray[0]+flagarray[1]*4+flagarray[2]*4^2+...)
每一行flag的所有可能性最多为4^m,n行一共n*4^m。
以下为代码
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <queue>
#include <math.h>
using namespace std;
int p[8][8];
int dp[8][65536];
int n;
int m;
int m2;
int ercifang[]= {1,2,4,8,16,32,64,128};
//#define debug 0
int get1s(int value)
{
int res=0;
while(value)
{
if(value&1)
res++;
value>>=1;
}
return res;
}
//flagarray[i] 0 上一行i处为空 1上一行i处为未覆盖点 2上一行i处为覆盖点 3上一行i处为像素中心点
int testvalueflag(int line,int value,int flagarray[])
{
for(int i=0; i<m; i++)
{
if(flagarray[i]==0&&(value&ercifang[i]))
return 0;
if(flagarray[i]==1&&((value&ercifang[i])==0))
return 0;
if(p[line][i]==0)
{
if(i>=1&&(value&ercifang[i-1]))
return 0;
if(i<m-1&&(value&ercifang[i+1]))
return 0;
if(value&ercifang[i])
return 0;
if(flagarray[i]==3)
return 0;
}
else if(line==n-1)
{
if(i>=1&&(value&ercifang[i-1]))
continue;
if(i<m-1&&(value&ercifang[i+1]))
continue;
if(value&ercifang[i])
continue;
if(flagarray[i]==3)
continue;
return 0;
}
}
return 1;
}
int run(int line,int flag)
{
if(dp[line][flag]>=0)
return dp[line][flag];
int res=1000000;
#ifdef debug
int lastvaluearray[8];
#endif
int flagarray[8];
int flagcopy=flag;
memset(flagarray,0,8*sizeof(int));
for(int i=0; i<8; i++)
{
flagarray[i]=flagcopy&3;
flagcopy>>=2;
}
for(int value=0; value<m2; value++)
{
if(testvalueflag(line,value,flagarray))
{
int rest;
if(line==n-1)
rest=0;
else
{
int nextflagarraay[8];
memset(nextflagarraay,0,8*sizeof(int));
//flag 0 上一行此处为空 1上一行此处为未覆盖点 2上一行此处为覆盖点 3上一行此处为像素中心点
for(int i=0; i<m; i++)
{
if(p[line][i]==0)
nextflagarraay[i]=0;
else
{
if(value&ercifang[i])
nextflagarraay[i]=3;
else if(flagarray[i]==3)
nextflagarraay[i]=2;
else if(i>=1&&(value&ercifang[i-1]))
nextflagarraay[i]=2;
else if(i<m-1&&(value&ercifang[i+1]))
nextflagarraay[i]=2;
else nextflagarraay[i]=1;
}
}
int nextflag=0;
for(int i=m-1; i>=0; i--)
{
nextflag<<=2;
nextflag|=nextflagarraay[i];
}
rest=run(line+1,nextflag);
}
int thisline=get1s(value);
if(res>rest+thisline)
{
#ifdef debug
int valuecopy=value;
memset(lastvaluearray,0,8*sizeof(int));
for(int i=0; i<8; i++)
{
lastvaluearray[i]=valuecopy%2;
valuecopy/=2;
}
#endif
res=rest+thisline;
}
}
}
dp[line][flag]=res;
#ifdef debug
cout<<"line"<<line<<"\nflag:";
for(int i=0; i<8; i++)
cout<<flagarray[i]<<' ';
cout<<endl;
cout<<"value";
for(int i=0; i<8; i++)
cout<<lastvaluearray[i]<<' ';
cout<<endl<<"res"<<res<<endl;
#endif
return res;
}
int main()
{
#ifdef debug
freopen("in.txt","r",stdin);
#endif
cin>>n>>m;
memset(dp,0xff,8*65536*sizeof(int));
for(int i=0; i<n; i++)
{
char str[10];
scanf("%s",str);
for(int j=0; j<m; j++)
{
if(str[j]=='.')
p[i][j]=0;
else
p[i][j]=1;
}
}
if(n<m)
{
for(int i=0; i<8; i++)
for(int j=0; j<i; j++)
swap(p[i][j],p[j][i]);
swap(n,m);
}
m2=pow(2,m);
//43686=2+2*4+2*4^2+...+2*4^7
int ans=run(0,43690);
if(ans>64)
printf("-1");
else
printf("%d",ans);
return 0;
}