poj 3133 -- Manhattan Wiring 插头dp

颓了几天= =终于解决这道题了。庆祝一下。题解明天再补。



题意:棋盘上有两个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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值