bzoj 1814: Ural 1519 Formula 1【插头dp】

设f[i][j][s]为轮廓线推到格子(i,j),状态为s的方案数

括号表示一段线的左端和右端,表示成左括号和右括号,状压的时候用1和2表示,0表示已经闭合

下面的蓝线是黄色格子的轮廓线,dp转移要把它转到橙色轮廓线,设已经在状压的s中取到两条边的状态记为b1,b2

然后分很多情况讨论:

(i,j)是障碍:那就只能什么都不放的转移,也就是只能从b1=0,b2=0转移到新轮廓线的b1=0,b2=0

if(!a[i][j])
                {
                    if(!b1&&!b2)
                        add(x,v);
                }

b1=0,b2=0:因为不能空,所以只能转移一个拐角

else if(!b1&&!b2)
                {
                    if(a[i+1][j]&&a[i][j+1])
                        add(x+b[j-1]+2*b[j],v);
                }

b1=0或者b2=0:根据有无障碍判断能不能转移,如果(i,j+1),(i+1,j)都没有障碍的话就有两种转移,以b1=0,b2!=0为例:

一种是接上然后拐弯,这样转移后的轮廓线括号状态不变

另一种是接上直着走,转移后的轮廓线括号状态b1b2互换

b1!=0,b2=0同理

else if(!b1&&b2)
                {
                    if(a[i][j+1])
                        add(x,v);
                    if(a[i+1][j])
                        add(x-b[j]*b2+b[j-1]*b2,v);
                }
                else if(b1&&!b2)
                {
                    if(a[i][j+1])
                        add(x-b[j-1]*b1+b[j]*b1,v);
                    if(a[i+1][j])
                        add(x,v);
                }

b1=b2=1或2:这样这两条线会在(i,j)格子连起来,两队括号合成一对,以b1=b2=1为例:

else if(b1==1&&b2==1)
                {
                    for(int t=1,l=j+1;l<=m;l++)
                    {
                        if((x>>(l*2))%4==1)
                            t++;
                        if((x>>(l*2))%4==2)
                            t--;
                        if(!t)
                        {
                            add(x-b[j]-b[j-1]-b[l],v);
                            break;
                        }
                    }
                }
                else if(b1==2&&b2==2)
                {
                    for(int t=1,l=j-2;l>=0;l--)
                    {
                        if((x>>(l*2))%4==1)
                            t--;
                        if((x>>(l*2))%4==2)
                            t++;
                        if(!t)
                        {
                            add(x+b[l]-2*b[j]-2*b[j-1],v);
                            break;
                        }
                    }
                }

b1=2,b2=1:和上面差不多,就是把这两个括号合并就行了

else if(b1==2&&b2==1)
                    add(x-2*b[j-1]-b[j],v);

b1=1,b2=2:这个只有到最后一个没障碍的点才能转移,因为这是把一条线连成一个回路的最后一步

其实不用转移,直接加进答案就行了

else if(i==tx&&j==ty)
                    ans+=v;
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=15,mod=299989;
int n,m,la,nw,a[N][N],b[N],c[2],h[300005],tx,ty;
long long ans;
char s[N];
struct qwe
{
    int ne,to[2];
    long long va[2];
}e[300005];
void add(int x,long long v)
{
    int u=x%mod+1;
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].to[nw]==x)
        {
            e[i].va[nw]+=v;
            return;
        }
    e[++c[nw]].ne=h[u];
    e[c[nw]].to[nw]=x;
    e[c[nw]].va[nw]=v;
    h[u]=c[nw];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;j++)
            if(s[j]==‘.‘)
                a[i][j]=1,tx=i,ty=j;
    }
    b[0]=1;
    for(int i=1;i<=12;i++)
        b[i]=b[i-1]<<2;
    c[0]=1,e[1].va[0]=1,e[1].to[0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c[nw];j++)
            e[j].to[nw]<<=2;
        for(int j=1;j<=m;j++)
        {
            la=nw,nw^=1;
            memset(h,0,sizeof(h));
            c[nw]=0;
            for(int k=1;k<=c[la];k++)
            {
                int x=e[k].to[la],b1=(x>>(j*2-2))%4,b2=(x>>(j*2))%4;
                long long v=e[k].va[la];
                if(!a[i][j])
                {
                    if(!b1&&!b2)
                        add(x,v);
                }
                else if(!b1&&!b2)
                {
                    if(a[i+1][j]&&a[i][j+1])
                        add(x+b[j-1]+2*b[j],v);
                }
                else if(!b1&&b2)
                {
                    if(a[i][j+1])
                        add(x,v);
                    if(a[i+1][j])
                        add(x-b[j]*b2+b[j-1]*b2,v);
                }
                else if(b1&&!b2)
                {
                    if(a[i][j+1])
                        add(x-b[j-1]*b1+b[j]*b1,v);
                    if(a[i+1][j])
                        add(x,v);
                }
                else if(b1==1&&b2==1)
                {
                    for(int t=1,l=j+1;l<=m;l++)
                    {
                        if((x>>(l*2))%4==1)
                            t++;
                        if((x>>(l*2))%4==2)
                            t--;
                        if(!t)
                        {
                            add(x-b[j]-b[j-1]-b[l],v);
                            break;
                        }
                    }
                }
                else if(b1==2&&b2==2)
                {
                    for(int t=1,l=j-2;l>=0;l--)
                    {
                        if((x>>(l*2))%4==1)
                            t--;
                        if((x>>(l*2))%4==2)
                            t++;
                        if(!t)
                        {
                            add(x+b[l]-2*b[j]-2*b[j-1],v);
                            break;
                        }
                    }
                }
                else if(b1==2&&b2==1)
                    add(x-2*b[j-1]-b[j],v);
                else if(i==tx&&j==ty)
                    ans+=v;
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/lokiii/p/10117582.html

时间: 2024-08-28 02:39:06

bzoj 1814: Ural 1519 Formula 1【插头dp】的相关文章

bzoj 1814: Ural 1519 Formula 1 插头dp经典题

用的括号序列,听说比较快. 然并不会预处理,只会每回暴力找匹配的括号. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define N 199917 6 #define ll long long 7 #define bp 1<<bit[j-1] 8 #define bq 1<<bit[j] 9 using nam

【bzoj1814】Ural 1519 Formula 1 插头dp

题目描述 一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数. 输入 The first line contains the integer numbers N and M (2 ≤ N, M ≤ 12). Each of the next N lines contains M characters, which are the corresponding cells of the rectangle. Character "." (full stop)

【URAL 1519】【插头dp模板】Formula 1

1519. Formula 1 Time limit: 1.0 second Memory limit: 64 MB Background Regardless of the fact, that Vologda could not get rights to hold the Winter Olympic games of 20**, it is well-known, that the city will conduct one of the Formula 1 events. Surely

[URAL 1519] Formula 1 轮廓线DP 括号序列

题意 n * m 的矩形, 有坏点, 问哈密顿回路数量. n, m <= 11 . 分析 1. 确立状态 我们考虑轮廓线DP. 为此, 我们要刻画并量化轮廓线的相关信息: ① 插头是否存在? ② 插头的连通性. 我们发现: 插头一一对应, 且互不相交. 于是考虑使用括号序列刻画轮廓线. 至于量化, 我们将一个括号序列当做一个三进制数即可. 2. 转移 从上一行的最后一个, 转移到当前行的第一个: 位移. 当前格子为坏点: 对于没有插头插到当前点的状态原样复制. 否则: (1) L = # , R

bzoj1814: Ural 1519 Formula 1 2011-12-20

1814: Ural 1519 Formula 1Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 263  Solved: 70[Submit][Status][Discuss]DescriptionRegardless of the fact, that Vologda could not get rights to hold the Winter Olympic games of 20**, it is well- known, that the 

URAL - 1519 Formula 1 (插头DP)

这里写链接内容 刚开始学插头dp好吃力,看了别人的代码有点看不懂,所以就参考了别人的代码,写了注释,希望有帮助 如果看不懂可以问 //下面所说的情况全在论文中的第13页 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 13 #define S1 14000 #define S2 1600000 struct Queue{ int opt; long

URAL 1519 Formula 1 (插头DP,常规)

题意:给一个n*m的矩阵,格子中是'*'则是障碍格子,不允许进入,其他格子都是必走的格子,所走格子形成一条哈密顿回路,问有多少种走法? 思路: 本来是很基础的题,顿时不知道进入了哪个坑.这篇插头DP的文章够详细,推荐一看(最好不要照抄代码). 细节要小心,比如输出用I64d,必须用long long,判断是否无哈密顿回路,位操作等等. 这次仍然用括号表示法,1表示(,2表示). 1 #include <bits/stdc++.h> 2 #define pii pair<int,int&g

Ural1519 Formula 1(插头dp)

原题网址:http://acm.timus.ru/problem.aspx?space=1&num=1519 有关插头dp和状态压缩请参考:http://wenku.baidu.com/link?url=AFuYe_EfR5yXMNK0rY-TaLe6LLgKhsOVxBM1RQULxElPrvjQVlO724nUxlXtaDx4aLp7FHIz8AexYiTy06_r4CV5XUs6c9lM5vpz5kDr6HG 详细代码(c++): 1 #include <cstdio> 2 #i

[URAL1519] Formula 1 [插头dp入门]

题面: 传送门 思路: 先理解一下题意:实际上就是要你求这个棋盘中的哈密顿回路个数,障碍不能走 看到这个数据范围,还有回路处理,就想到使用插头dp来做了 观察一下发现,这道题因为都是回路,所以联通块上方的插头一定两两配对,可以使用括号序列代替最小表示法 分情况讨论一下 情况一:当前格子上方和左方都没有插头 这种时候可以继续,也可以给当前格子加一个下插头一个右插头,相当于一个新的联通分量 情况二:上方有一个下插头,左边没有 这时有两个决策:可以向右转,也可以继续向下,操作就是分别给这个格子一个右插