poj 3133&&LA 3620 Manhattan Wiring 插头dp

本文介绍了一种解决棋盘上特定数字格子相连问题的算法,通过状态压缩和动态规划方法找到两条不相交连线的最短总长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

在棋盘里,两个格子标为2,两个格子标为3,障碍格标为1,空格标为0。现在要把两个2相连,两个3相连,这两条线不能交叉,问最短线长。

这里写图片描述


解:

对于标有数字的格子,只有4种连法:上、左、下、右

对于障碍格子,不能连任何线,只有一种连法:空。

对于空格子,有7种连法,其中一种是空。
(上下、左右、上左、上右、下左、下右、空)

只要保证有数字的格子只用4种连法、’2’线和’3’线不相交并且路径最短,就自然保证了只有一条2线,只有一条3线。

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define mes(a,x,s)  memset(a,x,(s)*sizeof a[0])
#define mem(a,x)  memset(a,x,sizeof a)
#define ysk(x)  (1<<(x))
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const int maxn= 9   ;
const int maxS=  1048576   ;//4^10
const int mod=19993;
int a[maxn+3][maxn+3];
int n,m,cur;

struct HashMap
{
    vector<pii>G[mod+10];
    void init() {  for0(i,mod) G[i].clear(); }
    void insert(int state,int val)
    {
        int p=state%mod;
        for0(i,G[p].size())
        {
            if(G[p][i].first==state)
            {
                if(val<G[p][i].second) G[p][i].second=val;
                return;
            }
        }
        G[p].push_back(make_pair(state,val));
    }


}hashmap[2];

struct Code
{
   int bit[12],s;
   void init(int x)
   {
       for(int i=0;i<=m;i++)
       {
           bit[i]=x&3;
           x>>=2;
       }

   }
   void getS()
   {
       s=0;
       for(int i=m;i>=0;i--)
       {
           s=(s<<2)|bit[i];
       }
   }
   void shift()
   {
       for(int i=m;i>=1;i--) bit[i]=bit[i-1];
       bit[0]=0;
   }


}code;



void dp1(int x,int y,int state,int val)
{
    const int p=code.bit[y-1],q=code.bit[y];
    if(y==m)  code.shift();
    code.getS();
    hashmap[cur].insert(code.s,val);

}

void dp23(int x,int y,int state,int val)
{
    const int p=code.bit[y-1],q=code.bit[y];
    int now=a[x][y]-1;
    if(p&&q) return;
    if(p||q)
    {
        if(p+q!=now)  return;
        code.bit[y-1]=code.bit[y]=0;
        if(y==m)   code.shift();
        code.getS();
        hashmap[cur].insert(code.s,val+1);
        return;
    }
    int t=a[x][y]-1;
    if(y+1<=m&&a[x][y+1]!=1)
    {
        code.bit[y-1]=0,code.bit[y]=t;
        code.getS();
        hashmap[cur].insert(code.s,val+1);
    }
    if(x+1<=n&&a[x+1][y]!=1)
    {
        code.bit[y-1]=t,code.bit[y]=0;
        if(y==m) code.shift();
        code.getS();
        hashmap[cur].insert(code.s,val+1);
    }

}

void dp0(int x,int y,int state,int val)
{
     const int p=code.bit[y-1],q=code.bit[y];
     if(!p&&!q)
     {
         if(y+1<=m&&a[x][y+1]!=1&&x+1<=n&&a[x+1][y]!=1)
         {
             code.bit[y-1]=code.bit[y]=1;
             code.getS();
             hashmap[cur].insert(code.s,val+2);
             code.bit[y-1]=code.bit[y]=2;
             code.getS();
             hashmap[cur].insert(code.s,val+2);
         }

         code.bit[y-1]=code.bit[y]=0;
         if(y==m) code.shift();
         code.getS();
         hashmap[cur].insert(code.s,val);
         return;

     }
     if(p&&q)
     {
         if(p!=q) return;
         code.bit[y-1]=code.bit[y]=0;
         if(y==m) code.shift();
         code.getS();
         hashmap[cur].insert(code.s,val+2);
         return;
     }
     int t=p+q;
     if(y+1<=m&&a[x][y+1]!=1)
     {
         code.bit[y-1]=0,code.bit[y]=t;
         code.getS();
         hashmap[cur].insert(code.s,val+2);
     }
     if(x+1<=n&&a[x+1][y]!=1)
     {
         code.bit[y-1]=t,code.bit[y]=0;
         if(y==m) code.shift();
         code.getS();
         hashmap[cur].insert(code.s,val+2);
     }


}
void solve()
{
    cur=0;
    hashmap[cur].init();
    hashmap[cur].insert(0,0);
    for1(i,n) for1(j,m)
    {
       cur^=1;
       hashmap[cur].init();
       for0(k,mod )  for0(l,hashmap[cur^1].G[k].size())
       {
           pii now=hashmap[cur^1].G[k][l];
           int state=now.first;
           int val=now.second;
           code.init(state);
           if(a[i][j]==2||a[i][j]==3)  dp23(i,j,state,val);
           else   if(a[i][j]==1)  dp1(i,j,state,val);
           else    dp0(i,j,state,val);
       }
    }
    int ans=INF;
    for0(k,mod)  for0(l,hashmap[cur].G[k].size())
    {
        pii now=hashmap[cur].G[k][l];
        ans=min(ans,now.second/2);
    }
    if(ans==INF) ans=0;
    cout<<ans<<endl;
}
int main()
{
   std::ios::sync_with_stdio(false);
   while(cin>>n>>m&&(n||m))
   {
       for1(i,n) for1(j,m) cin>>a[i][j];
       solve();
   }
   return 0;
}


后记:

为什么此题不用最小表示法,formula1中使用最小表示法,是因为在很多地方都可以新建连通分量,(这些分量到最后要求融合),而在最后一个非障碍格子,需要判断上左插头是不是在同一个连通分量里,进而判断添加添加上左插头是不是能形成一个闭合回路。
在最小表示法中,上左均有插头并且编号相同的情况以为着某个闭合回路要形成了,这种情况只能出现在最后一个非障碍格子里,否则会出现形成多个回路的情况。(而且,对于最后一个非障碍格子,因为下右均是障碍格子或者出界,所以能转移的情况只有上、左有插头,并且只有当两插头编号相同才是合法的)

这个题目因为要求比较特殊,新建连通分量只能在2和3这几个特定的格子里,最主要是要求路径最短,所以形成多个回路一定不是最优解,所以无需用最小表示法,直接用3进制即可。

在formula1中,最后一个非障碍格子里出现上左插头,并且编号不一样的情况似乎绝不会发生,貌似因为这代表前面的插头并没有处理(衔接)好:因为两个连通分量在这个位置第一次接触,那么前面必然没有接触,那么这两根线(插头)的另一端接到哪去了?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值