URAL 1519 Formula 1 dp(插头)

这里写图片描述


题意:

给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路。问有多少种铺法?


解:

括号表示法:

4进制状压表示,用m+1位表示轮廓线上的m个位置的情况。

0表示没有插头与线相交,1表示插头’左’,2表示插头’右’。类似括号匹配。

重要的一点是轮廓线上方的线路不能相交。

转移:

1.当前格上左无插头,则该格下右建插头,新的连通分量。

2.上或左有插头,必连下或右,仅一个。

3.上左均有插头,考虑连通分量的消除。

轮廓线上需要关心的只是哪些线上面有插头,

1情况是起点,3情况包含了终点,但不仅仅是终点。

固定了起点和终点,所以能保证只有两条线,只要转移时能保证这两条线

不相交并且遍历完所有无障碍格子即可。(保证两线从起点出发,到终点汇

合)

到行末时,状态需要*4(因为是四进制),然后可以考虑CLEAR(m+1)

位。


#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<ll, ll> pll;
const int INF =0x3f3f3f3f;
const int maxn=12    ;
const int maxV=1e6    ;
const int mod=19993;
int n,m,nn,mm;
int a[maxn+10][maxn+10];
int cur;
int state[2][maxV];
ll sum[2][maxV];
int tot[2],HASH[maxV];
int pre[maxn+10];

void pr()
{
    pre[0]=1;
    for1(i,maxn+3)
    {
        pre[i]=ysk((i<<1));
    }
}

void insert(int S,ll nu)
{
    int p=S%mod;
    while(~HASH[p])
    {
        if(state[cur][HASH[p]]==S)
        {
            sum[cur][HASH[p]]+=nu;
            return;
        }
        if(++p==mod) p=0;
    }
    HASH[p]=tot[cur];
    state[cur][tot[cur]]=S;sum[cur][tot[cur]++]=nu;
}

void show(ll s)
{
    cout<<"show"<<s<<endl;

    for(int i=0;i<=m;i++)
    {
       ll x=s/pre[i];
       x%=4;
       cout<<x;
    }
    cout<<endl;
}
void solve()
{
    ll ans=0;
    cur=0;
    mem(state[cur],0);
    mem(sum[cur],0);
    tot[cur]=0;
    mem(HASH,-1);
    tot[cur]=1;sum[cur][0]=1;
    state[cur][0]=0;

    for1(i,n)
    {
        for1(j,m)
        {
            cur^=1;
            mem(state[cur],0);
            mem(sum[cur],0);
            tot[cur]=0;
            mem(HASH,-1);
//            printf("i=%d,j=%d,tot^1=%d\n",i,j,tot[cur^1]);
            for0(k,tot[1^cur])
            {
                int S=state[1^cur][k];
                ll nu=sum[1^cur][k];
                int p=(S/pre[j-1])%4;
                int q=(S/pre[j])%4;
//                printf("p=%d,q=%d,S=%lld\n",p,q,S);
//                show(S);



                if(!a[i][j])
                {
                     if(!p&&!q) insert(S,nu);
                     continue;
                }

                int S2;
                if(!p&&!q)
                {
                    if(a[i][j+1]&&a[i+1][j])
                    {
                       S2=S+pre[j-1]+2*pre[j] ;
                       insert(S2,nu);
                    }
                }
                else if(!p&&q)
                {
                   if(a[i][j+1]) insert(S,nu);
                   if(a[i+1][j])
                   {
                      S2=S-q*pre[j]+q*pre[j-1];
                      insert(S2,nu);
                   }

                }
                else if(p&&!q)
                {
                    if(a[i+1][j])  insert(S,nu);
                    if(a[i][j+1])
                    {
                        S2=S-p*pre[j-1]+p*pre[j];

                        insert(S2,nu);
                    }

                }
                else if(p==1&&q==1)
                {
                    int cnt=1;
                   for(int pp=j+1;pp<=m;pp++)
                   {
                       if( (S/pre[pp])%4 ==1  )  cnt++;
                       if( (S/pre[pp])%4 ==2  )  cnt--;
                       if(cnt==0)
                       {
                           S2=S-2*pre[pp]+1*pre[pp];
                           break;
                       }
                   }
                   S2=S2-pre[j-1]-pre[j];
                   insert(S2,nu);
                }
                else if(p==2&&q==2)
                {
                   int cnt=1;
                   for(int pp=j-2;pp>=0;pp--)
                   {
                       if( (S/pre[pp])%4 ==1  )  cnt--;
                       if( (S/pre[pp])%4 ==2  )  cnt++;
                       if(cnt==0)
                       {
                           S2=S+2*pre[pp]-1*pre[pp];
                           break;
                       }
                   }
                   S2=S2-2*pre[j-1]-2*pre[j];
                   insert(S2,nu);

                }
                else if(p==2&&q==1)
                {
                     S2=S-2*pre[j-1]-pre[j];
                     insert(S2,nu);
                }
                else if(p==1&&q==2)
                {
                    if(i==nn&&j==mm) ans+=nu;
                }


              }

        }
         for(int k=0;k<tot[cur];k++)
         {
             state[cur][k]<<=2;
             while(state[cur][k]>=pre[m+1]) state[cur][k]-=pre[m+1];
         }
    }
    cout<<ans<<endl;

}
int main()
{
   std::ios::sync_with_stdio(false);
   char ch;
   pr();
   while(cin>>n>>m)
   {
       mem(a,0);
       for1(i,n)
       {
           for1(j,m)
           {
               cin>>ch;
               a[i][j]=ch=='.'?1:0;
               if(a[i][j])
               {
                   nn=i,mm=j;
               }
           }
       }
       solve();
   }
   return 0;
}
/*
4 4
**..
....
....
....
*/
/*
1 4
....


1 1
.

1 2
..
2 2
..
..
*/

最小表示法:

8进制表示,每一个连通分量用数字表示,0表示没有插头。
因为只能有一条线,即轮廓线上方不能分叉,而且轮廓线最多13格,其中有一条是竖线,,可以发现最多到数字6。
可以用7进制表示,但是8进制能用位运算,更快。

对于当前格子,设p=左插头编号,q=上插头编号,如果为0证明没有插头。

1)
如果p==0&&q==0 ,那么新建连通分量,轮廓线上变为下插头+右插头。

2)
如果!p&&q||p&&!q ,考虑右插头或者下插头,不能同时有。

3)
如果p&&q ,

如果p==q,此时只能在最后一个非障碍格子才能进行状态转移。

否则将左插头的连通分量改为上插头编号。

对于情况3),如果是最后一个非障碍格子,只有当上左插头的编号相同时才纳入ans的计算。

这个题目要注意没有非障碍格子的情况。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define mem(a,x)  memset(a,x,sizeof a)
using namespace std;
const int maxn=12;
const int mod=19993;
typedef long long ll;
typedef pair<ll ,ll>pll;
int cur,nn,mm,n,m,a[maxn+3][maxn+3];
ll ans;
struct Hash
{
    vector<pll>G[mod+3];
    void init()  { for0(i,mod) G[i].clear();}
    void insert(ll s,ll val)
    {
        int p=s%mod;
        for0(i,G[p].size())
        {
            if(G[p][i].first==s)  {
                G[p][i].second+=val;return;
            }
        }
        G[p].push_back(make_pair(s,val));
    }

}hashmap[2];

struct Code
{
    int bit[15],tot;
    int ch[15];
    ll s;
    void init(ll x)
    {
        tot=0;
        for0(i,m+1)
        {
           bit[i]=x&7;
           tot=max(tot,bit[i]);
           x>>=3;
        }
        tot++;
    }
    void normalize()
    {
        mem(ch,-1);
        ch[0]=0;tot=1;
        s=0;
        for(int i=m;i>=0;i--)
        {
            if(~ch[bit[i]])  bit[i]=ch[bit[i] ];
            else{
                bit[i]=ch[bit[i] ]=tot++;
            }
            s=(s<<3)+bit[i];
        }
    }
    void color(int a,int b)//a to b
    {
        for0(i,m+1) if(bit[i]==a) bit[i]=b;
    }
    void shift()
    {
        for(int i=m;i>=1;i--) bit[i]=bit[i-1];
        bit[0]=0;
    }

}code;


void updateBlock(int x,int y,ll state,ll val)
{
        if(y==m) code.shift();
        code.normalize();
        hashmap[cur].insert(code.s,val);
}

void updateBlank(int x,int y,ll state,ll val)
{

        const int p=code.bit[y-1];
        const int q=code.bit[y];
        if(!p&&!q)
        {
            if(a[x][y+1]&&a[x+1][y])
            {
                code.bit[y-1]=code.bit[y]=code.tot++;
                code.normalize();
                hashmap[cur].insert(code.s,val);
            }
        }
        else  if(p&&!q||!p&&q)
        {
            int t=p+q;
            if(a[x][y+1])
            {
                code.bit[y-1]=0,code.bit[y]=t;
                code.normalize();
                hashmap[cur].insert(code.s,val);
            }
            if(a[x+1][y])
            {
                code.bit[y-1]=t,code.bit[y]=0;
                if(y==m)  code.shift();
                code.normalize();
                hashmap[cur].insert(code.s,val);
            }
        }
        else
        {
            if(p==q)
            {
                if(x==nn&&y==mm)
                {
                    code.bit[y-1]=code.bit[y]=0;
                    if(y==m)  code.shift();
                    code.normalize();
                    hashmap[cur].insert(code.s,val);
                    ans+=val;
                }
            }
            else
            {
                code.color(p,q);
                code.bit[y-1]=code.bit[y]=0;
                if(y==m)  code.shift();
                code.normalize();
                hashmap[cur].insert(code.s,val);
            }
        }

}
void solve()
{
    ans=cur=0;
    hashmap[cur].init();
    hashmap[cur].insert(0,1);
    for1(i,n)
    {
        for1(j,m)
        {
            cur^=1;hashmap[cur].init();
            for0(k,mod) for0(l,hashmap[cur^1].G[k].size()){
             pll now=hashmap[cur^1].G[k][l];
             ll state=now.first,val=now.second;
             code.init(state);
            if(a[i][j])  updateBlank(i,j,state,val);
            else   updateBlock(i,j,state,val);
            }
        }
    }
    cout<<ans<<endl;
}
int main()
{
    while(cin>>n>>m)
    {
       mem(a,0);nn=mm=-1;
       char ch;
       for1(i,n) for1(j,m) {
           cin>>ch;
           if(ch=='.')  {
                a[i][j]=1;
                nn=i,mm=j;
           }
       }
       if(nn==-1) {
        puts("0");
        continue;
       }
       solve();
    }
    return 0;
}
/*
2 2
..
..
2 3
...
...
3 2
..
..
..
12 12
............
............
............
............
............
............
............
............
............
............
............
............
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值