确实是好题。。。看解题报告看了一天半,才大概明白是怎么样一个过程。最让我纠结的是M,N的行列问题。。。。文字描述里的M,N和图片上的M,N不对应。本菜只能无比蛋疼而又晕晕乎乎的看。。。
-------------------------------以下部分转自大牛chinaeli-----------------------------------------http://hi.baidu.com/chinaeli/blog/item/b932d4b430d60ac636d3ca34.html
好了,废话说道这里,我来说说我的想法(其实这个方法都是大家用的,我也是效仿别人的方法)
首先说一下如何用三进制表示状态:
假设现在考虑(m,i),从而推出(m,i+1)的状态,因此我们需要记录第i列以及第i-1列的状态,用三进制表示的方法就是A(用0表示),B(用1表示),C(用2表示),其中虚线部分为第i+1列(这个图片旋转90度比较好,做图片时粗心了,嘻嘻),C状态中,因为假如第i列不能放,那么自然第i-1列放和不放都无关紧要了,所以用灰色表示。用p(m)表示第m行,第i列和第i-1列的状态,那么整个的放置状态就是p(0)*3^0+p(1)*3^1......p(M-1)*3^(M-1),转成10进制存储。
状态表示完之后,就要进行状态转移了。
M位的三进制共有3^M种状态,( 0<M<=10),因此最大有59049种状态,采用滚动数组,只要开一个2*59049大小的二维数组就可以了。将边界条件f[0][3^M-1]=0,其余初始为-1,表示该状态不可到达或者尚未到达。然后从第0行开始考虑,枚举每行的状态( 从0到3^M-1 ) ,用s表示,将10进制数转换成3进制存在一个p数组中,接着需要产生一个新的状态,也就是第i+1列的状态:
由于下移了一列,第i-1列我们暂不考虑,之前的A状态变成了A',B变成了B',C变成了C’,之前的0和1都变成新状态的0,原来的2状态变成了新状态的1,但是需要考虑的是第i+1列中是否会存在不能放的区域,假如上图的某个问号中某处不能放置东西,那么不论这个状态是0,还是1,都将变成2,将新状态存于q数组中。现在新生成的状态t是在第i+1行不放置任何东西的状态,那么自然f[i+1][t]=f[i][s].
现在就要进入关键的部分了,用dfs搜索各种摆放的状态。
假设现在枚举到(m,n),目前状态是s,目前已经摆放了cnt个芯片。
如果m<M-2,说明还有至少3列的空处,可以摆放A方式的芯片,但是必须满足(m,n),(m,n-1),(m+1,n),(m+1,n-1),(m+2,n),(m+2,n-1)处空白,也就是q[m]==0 && q[m+1]==0&& q[m+2]==0.那么将状态修改为s=s+2*3^m+2*3^(m+1)+2*3^(m+2)。同理进行B方式的摆放,不过在摆放B时,需要考虑到第n-2行,所以要保证p[m]==0,p[m+1]==0。
当然这一格也可以不摆放。
#include<stdio.h> #include<string.h> #include<stdlib.h> #define Max(a,b) (a>b?a:b) #define N 59050 //三进制 int tri[12]={0,1,3,9,27,81,243,729,2187,6561,19683,59049}; int map[151][11]; int dp[2][N]; //前一状态,当前状态 int info_pre[11],info_cur[11]; int n,m,pass; //初始化 void Init() { memset(map,0,sizeof(map)); memset(dp,-1,sizeof(dp)); } //转化 十进制 int switch_ten(int *p) { int i,ans; for(i=1,ans=0;i<=m;i++){ ans += p[i]*tri[i]; } return ans; } //转化 三进制 void switch_tri(int t,int *p) { int i; for(i=1;i<=m;i++){ p[i] = t%3; t/=3; } return ; } void dfs(int i,int j,int cnt,int stata) { int k; dp[i%2][stata] = Max(dp[i%2][stata],cnt); if(j >= m ) return ; if(!info_pre[j] && !info_pre[j+1] && !info_cur[j] && !info_cur[j+1]){ // 3*2 排列时 info_cur[j] = info_cur[j+1] = 2; k = switch_ten(info_cur); dfs(i,j+2,cnt+1,k); info_cur[j]=info_cur[j+1]=0; } if(j < m-1 && !info_cur[j] && !info_cur[j+1] && !info_cur[j+2]){ // 2*3 排列时 info_cur[j] = info_cur[j+1] = info_cur[j+2] = 2; k = switch_ten(info_cur); dfs(i,j+3,cnt+1,k); info_cur[j] = info_cur[j+1] = info_cur[j+2] = 0; } dfs(i,j+1,cnt,stata); return ; } int main() { int t,k,i,j,l,stata,ans; scanf("%d",&t); while(t--){ Init(); scanf("%d%d%d",&n,&m,&k); while(k--){ scanf("%d%d",&i,&j); map[i][j] = 1; } for(i=1;i<=m;i++){ info_pre[i] = map[1][i] + 1; } stata = switch_ten(info_pre); dp[1][stata] = 0; for(i=2;i<=n;i++){ for(j=0;j<tri[m+1];j++)dp[i%2][j] = -1; for(j=0;j<tri[m+1];j++){ if(dp[(i+1)%2][j] == -1)continue; switch_tri(j,info_pre); for(l=1;l<=m;l++){ if(map[i][l]) info_cur[l] = 2; else info_cur[l] = Max(info_pre[l]-1,0); } pass = dp[(i+1)%2][j]; dfs(i,1,pass,switch_ten(info_cur)); } } for(i=0,ans=0;i<tri[m+1];i++){ ans = Max(ans,dp[n%2][i]); } printf("%d\n",ans); } return 0; }