颓了几天= =终于解决这道题了。庆祝一下。题解明天再补。
。
。
。
题意:棋盘上有两个2和两个3,以及一些障碍格子。障碍格不可经过。求分别用一根线将两个2,3连起来经过的最小格子数-2;无解输出0。
用0表示无插头。2表示连接2的插头。3表示连接3的插头。dp[i][j][k]表示决策到第(i,j)格,当前轮廓线状态为k时。经过格子的最小数。然后逐格递推即可。
接下来是递推时的分类讨论= =:
普通格:I. (当前决策格左上)有两个不同插头。跳过。
普通格:II.有两个相同相同插头。合并两个插头。
普通格:III.只有一个插头。
1、改为下插头。
2、改为右插头。
普通格:IV.没有插头。
1、不增加插头。
2、增加一对2插头。
障碍格:V.有插头。跳过
障碍格:VI.无插头。同IV.1。
2号格:VII.只有一个2插头。将2插头去掉(即接到当前格子上)。
2号格:VIII.没有插头。创建右插头或下插头。
3号格:IX.类似2号格。
(缩进竟然被吞了= =)
这题好像没什么要注意的细节。算比较基础的题了。
ps:因为卡空间所以要滚动数组。又要卡时间所以要写成滚动hash= =。。。
详见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define begin Begin
#define next Next
#define mk(a) memset(a,0x3f3f3f3f,sizeof(a))
using namespace std;
const int N=11,ST=1100000,MOD=60007;
int n,m,ans;
struct hahahahash
{
int begin[MOD+10],next[ST],to[ST],w[ST];
int e;
void add(int p,int x)
{
int mp=p%MOD+1;
for(int i=begin[mp];i;i=next[i])
if(to[i]==p)
{
w[i]=min(w[i],x);
return ;
}
to[++e]=p;
next[e]=begin[mp];
begin[mp]=e;
w[e]=x;
}
void init()
{
memset(begin,0,sizeof(begin));
e=0;
}
}h[2];
struct work_man
{
int map[N][N];
int L,U,l,u;
bool P;
void init(bool p)
{
h[p].init();
}
void read()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);
if(map[i][j]==1 || map[i][j]==0)
map[i][j]^=1;
}
}
int go(int y,int S,int v) //状态转移函数
{
//拜大神!
//Easy :)
//hello lyq!
int CN=L|U,cn=l|u;
if(v==1)return (S^CN)|(cn<<((y-1)<<1)); //将原有插头引向下方向
if(v==2)return (S^CN)|(cn<<(y<<1)); //将原有插头引向右方向
if(v==3)return S^CN; //合并两个相同插头 或将插头接到目标方块上
if(v==4)return S|(10<<((y-1)<<1)); //创建2插头
if(v==5)return S|(15<<((y-1)<<1)); //创建3插头
if(v==6)return S|(2<<((y-1)<<1)); //创建独立2插头
if(v==7)return S|(2<<(y<<1)); //同上
if(v==8)return S|(3<<((y-1)<<1)); //创建独立3插头
return S|(3<<(y<<1)); //同上
}
void running_god()
{
read();
init(P=0);
h[P].add(0,0);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
init(!P);
for(int v=1;v<=h[P].e;v++)
{
int k=h[P].to[v],j_=j;
bool flag=1; //判断是否为行末
if(j==m){k<<=2;j_++;flag=0;}
L=k&(3<<((j_-1)<<1)),U=k&(3<<(j_<<1)),l=L>>((j_-1)<<1),u=U>>(j_<<1);
//取出上插头和左插头的状态
if((l^u)==1)continue; //如果一个2一个3。跳过
int c=h[P].w[v];
if(!map[i][j])
{
if(l|u)continue;
h[!P].add(k,c);
}
else if(map[i][j]==1)
{
if(l&u)h[!P].add(go(j_,k,3),c+1);
else if(l|u)
{
h[!P].add(go(j_,k,1),c+1);
if(flag)h[!P].add(go(j_,k,2),c+1);
}
else
{
if(flag && map[i+1][j] && map[i][j+1])
{
h[!P].add(go(j_,k,4),c+1);
h[!P].add(go(j_,k,5),c+1);
}
h[!P].add(k,c);
}
}
else if(map[i][j]==2)
{
if(l|u)
{
if(l^u^2)continue;
h[!P].add(go(j_,k,3),c+1);
}
else
{
h[!P].add(go(j_,k,6),c+1);
if(flag)h[!P].add(go(j_,k,7),c+1);
}
}
else
{
if(l|u)
{
if(l^u^3)continue;
h[!P].add(go(j_,k,3),c+1);
}
else
{
h[!P].add(go(j_,k,8),c+1);
if(flag)h[!P].add(go(j_,k,9),c+1);
}
}
}
P^=1;
}
}
ans=-1;
for(int i=1;i<=h[P].e;i++)
if(h[P].to[i]==0)ans=h[P].w[i];
if(ans==-1)ans=0;
else ans-=2;
}
void work()
{
running_god();
printf("%d\n",ans);
}
}d;
int main()
{
//freopen("poj3133.in","r",stdin);
//freopen("poj3133.out","w",stdout);
while(scanf("%d%d",&n,&m),n|m)
d.work();
return 0;
}