题目大意:有一个n*m的矩阵,最开始每个格子里有且仅有一个蜘蛛,每只蜘蛛可以向上下左右四个反向移动,也可以不动,问最后,最多有多少空地。
思路:
因为蜘蛛只能移动一步,那么想到用轮廓线状压。用二进制状态j表示当前轮廓线状态,1表示有蜘蛛,0表示没有蜘蛛。因为蜘蛛还可以向下和右边走,一排轮廓线是不够的,那么,再用一维表示下一个轮廓线的状态,1表示有蜘蛛到这里,0表示没有蜘蛛到这里。那么dp[ i ][ j ][ k ]表示,到第i个点,当前轮廓线状态为j,下一轮廓线状态为k,最多的空的数目。
后来,我突然想到我以前做过的一个题。发现那个问题其实和这个问题基本一致。那个问题是这样的,棋盘上可以放一些棋子,棋子的攻击范围是其本身和周围四个点,现在要使棋盘的每个点都被攻击,且棋子不能互相攻击,问,最少放几个棋子。如果把棋子不能互相攻击这个条件忽略,其实这两个题,想达到的目的就是一样的。棋子的最小值 == n*m-空地的最大值。这题可以用3进制压缩做,只需要一排轮廓线,0表示暂时没有被攻击,1表示已经被攻击了,2表示这个点放的是棋子。
我本来以为后者的方法要更好的,但是因为3进制数没有位运算,结果效率基本一致 o(╯□╰)o 但是,如果范围扩大的话,肯定是3进制压缩更好。
第一种:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef long long LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=100011122;
const double INFF=1e100;
const double eps=1e-8;
const LL mod=1000000007;
const int NN=100010;
const int MM=1000010;
/* ****************** */
int dp[2][1<<6][1<<6];
int go0[1<<6];
void solve(int n,int m)
{
int t,i,j,k,mask=1<<m;
int zt1,zt2,temp,ii,jj,ad,ans;
for(i=0;i<2;i++)
for(j=0;j<mask;j++)
for(k=0;k<mask;k++)
dp[i][j][k]=-INF;
for(j=0;j<m;j++)
{
go0[j]=mask-1;
go0[j]=go0[j]-(1<<j);
}
t=0;
dp[t][mask-1][0]=0;
for(ii=1;ii<=n;ii++)
for(jj=0;jj<m;jj++)
{
t=1-t;
for(j=0;j<mask;j++)
for(k=0;k<mask;k++)
dp[t][j][k]=-INF;
for(j=0;j<mask;j++)
for(k=0;k<mask;k++)
if(dp[1-t][j][k]!=-INF)
{
temp=dp[1-t][j][k];
//bu dong
zt1=j|(1<<jj);
zt2=k&go0[jj];
dp[t][zt1][zt2]=max(dp[t][zt1][zt2],temp);
//shang
if(ii>1)
{
zt1=(j&go0[jj]) | ( (1<<jj)&k );
zt2=k&go0[jj];
if(zt1&(1<<jj))ad=0;
else ad=1;
if( ( j&(1<<jj) ) == 0 )ad--;
dp[t][zt1][zt2]=max(dp[t][zt1][zt2],temp+ad);
}
//xia
zt1=(j&go0[jj]) | ( (1<<jj)&k );
zt2=k|(1<<jj);
if(zt1&(1<<jj))ad=0;
else ad=1;
dp[t][zt1][zt2]=max(dp[t][zt1][zt2],temp+ad);
//zuo
if(jj>0)
{
zt1=(j&go0[jj]) | ( (1<<jj)&k );
zt1=zt1|( 1<<(jj-1) );
zt2=k&go0[jj];
if(zt1&(1<<jj))ad=0;
else ad=1;
if( ( j&( 1<<(jj-1) ) ) == 0 )ad--;
dp[t][zt1][zt2]=max(dp[t][zt1][zt2],temp+ad);
}
//you
if(jj+1<m)
{
zt1=(j&go0[jj]) | ( (1<<jj)&k );
zt2=k&go0[jj];
zt2=zt2|( 1<<(jj+1) );
if(zt1&(1<<jj))ad=0;
else ad=1;
dp[t][zt1][zt2]=max(dp[t][zt1][zt2],temp+ad);
}
}
}
ans=-INF;
for(j=0;j<mask;j++)
ans=max(ans,dp[t][j][0]);
printf("%d\n",ans);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
if(m>n)
swap(n,m);
solve(n,m);
return 0;
}
第二种,3进制压缩
/*
* this code is made by islands
* Problem: 1132
* Verdict: Accepted
* Submission Date: 2014-07-11 21:54:16
* Time: 48MS
* Memory: 9488KB
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef long long LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=100011122;
const double INFF=1e100;
const double eps=1e-8;
const LL mod=20120427;
const int NN=10;
const int MM=1000010;
/* ****************** */
int dp[2][800];
int pp3[15];
int a[15];
int temp[15];
void init(int t,int mask)
{
int i;
for(i=0;i<mask;i++)
dp[t][i]=INF;
}
int main()
{
int n,m;
int i,j,k,mask,zt;
int k1,t;
int ans;
bool fg;
pp3[0]=1;
for(i=1;i<=9;i++)
{
pp3[i]=pp3[i-1]*3;
}
scanf("%d%d",&n,&m);
if(m>n)swap(n,m);
mask=pp3[m];
t=0;
init(t,mask);
zt=0;
for(i=0;i<m;i++)
zt+=pp3[i];
dp[0][zt]=0;
// printf("mask==%d zt==%d\n",mask,zt);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
t=1-t;
init(t,mask);
for(k=0;k<mask;k++)
if(dp[1-t][k]!=INF)
{
zt=k;
a[0]=1;
for(k1=1;k1<=m;k1++)
{
a[k1]=zt%3;
zt/=3;
}
//fang attacker
for(k1=1;k1<=m;k1++)
temp[k1]=a[k1];
if(temp[j-1]==0)
temp[j-1]=1;
temp[j]=2;
zt=0;
for(k1=m;k1>=1;k1--)
{
zt=zt*3+temp[k1];
}
// printf("check i==%d j==%d %d %d\n",i,j,dp[t][zt],dp[1-t][k]+1);
dp[t][zt]=min(dp[t][zt],dp[1-t][k]+1);
//bu fang
if(a[j]==0)
continue;
for(k1=1;k1<=m;k1++)
temp[k1]=a[k1];
if(a[j]==2 || a[j-1]==2)
temp[j]=1;
else
temp[j]=0;
zt=0;
for(k1=m;k1>=1;k1--)
{
zt=zt*3+temp[k1];
}
// printf("check i==%d j==%d %d %d\n",i,j,dp[t][zt],dp[1-t][k]);
dp[t][zt]=min(dp[t][zt],dp[1-t][k]);
}
}
ans=INF;
for(k=0;k<mask;k++)
{
fg=false;
zt=k;
for(k1=1;k1<=m;k1++)
{
a[k1]=zt%3;
zt/=3;
if(a[k1]==0)
fg=true;
}
if(!fg)
ans=min(ans,dp[t][k]);
}
printf("%d\n",n*m-ans);
return 0;
}