zoukankan      html  css  js  c++  java
  • bzoj1814 Ural 1519 Formula 1(插头dp模板题)

    1814: Ural 1519 Formula 1

    Time Limit: 1 Sec  Memory Limit: 64 MB
    Submit: 924  Solved: 351
    [Submit][Status][Discuss]

    Description

     一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数

    Input

    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) means a cell, where a segment of the race circuit should be built, and character "*" (asterisk) - a cell, where a gopher hole is located.

    Output

    You should output the desired number of ways. It is guaranteed, that it does not exceed 2^63-1.

    Sample Input

    4 4
    **..
    ....
    ....
    ....

    Sample Output

    2
    分析:今天把插头dp学了一下,没想到dp还能写这么长......细节什么的也很多,在这里当一个模板吧.
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    
    const int maxn = 30010;
    const int pow[13] = {0,2,4,6,8,10,12,14,16,18,20,22,24};
    int n,m,now,pre,tx,ty;
    char map[20][20];
    
    struct node
    {
        int head[maxn],nextt[maxn],tot;
        ll sum[maxn],sta[maxn];
        void clear()
        {
            memset(head,-1,sizeof(head));
            tot = 0;
        }
        void push(ll x,ll v)
        {
            ll hashh = x % maxn;
            for (int i = head[hashh]; i >= 0; i = nextt[i])
            {
                if (sta[i] == x)
                {
                    sum[i] += v;
                    return;
                }
            }
            sta[tot] = x;
            sum[tot] = v;
            nextt[tot] = head[hashh];
            head[hashh] = tot++;
        }
    } f[2];
    
    int turnleft(ll x,int k)
    {
        return x << pow[k];
    }
    
    int get(ll x,int k)
    {
        return (x >> pow[k]) & 3;
    }
    
    ll del(ll x,int i,int j)
    {
        return x & (~(3 << pow[i])) & (~(3 << pow[j]));
    }
    
    int findr(ll x,int pos)
    {
        int cnt = 1;
        for (int i = pos + 1; i <= m; i++)
        {
            int k = get(x,i);
            if (k == 1)
                cnt++;
            else if (k == 2)
                cnt--;
            if (!cnt)
                return i;
        }
    }
    
    int findl(ll x,int pos)
    {
        int cnt = 1;
        for (int i = pos - 1; i >= 0; i--)
        {
            int k = get(x,i);
            if (k == 2)
                cnt++;
            else if (k == 1)
                cnt--;
            if (!cnt)
                return i;
        }
    }
    
    void solve2(int x,int y,int k)
    {
        int p = get(f[pre].sta[k],y - 1);   //右插头
        int q = get(f[pre].sta[k],y);   //下插头
        ll staa = del(f[pre].sta[k],y - 1,y);   //将这两个插头删掉以后的状态
        ll v = f[pre].sum[k];
        if (!p && !q)  //新建一个连通分量
        {
            if (map[x][y] == '*')
            {
                f[now].push(staa,v);
                return;
            }
            if (x < n && y < n && map[x + 1][y] == '.' && map[x][y + 1] == '.')
                f[now].push(staa | turnleft(1,y - 1) | turnleft(2,y),v);
        }
        else if (!p || !q)  //保持原来的连通分量
        {
            int temp = p + q;
            if (x < n && map[x + 1][y] == '.')
                f[now].push(staa | turnleft(temp,y - 1),v);
            if (y < m && map[x][y + 1] == '.')
                f[now].push(staa | turnleft(temp,y),v);
        }
        else if (p == 1 && q == 1)    //连接两个联通分量
            f[now].push(staa ^ turnleft(3,findr(staa,y)),v);  //这里的异或实际上就是把1变成2,2变成1
        else if (p == 2 && q == 2)
            f[now].push(staa ^ turnleft(3,findl(staa,y - 1)),v);
        else if (p == 2 && q == 1)
            f[now].push(staa,v);
        else if (x == tx && y == ty)
            f[now].push(staa,v);
    }
    
    ll solve()
    {
        f[0].clear();
        f[0].push(0,1);
        now = 0,pre = 1;  //滚动数组
        for (int i = 1; i <= n; i++)
        {
            pre = now;
            now ^= 1;
            f[now].clear();
            for (int k = 0; k < f[pre].tot; k++)
                f[now].push(turnleft(f[pre].sta[k],1),f[pre].sum[k]);   //左移一位,因为轮廓线下来的时候会少一个插头
            for (int j = 1; j <= m; j++)
            {
                pre = now;
                now ^= 1;
                f[now].clear();
                for (int k = 0; k < f[pre].tot; k++)
                    solve2(i,j,k);  //处理第k个状态
            }
        }
        for (int i = 0; i < f[now].tot; i++)
            if (f[now].sta[i] == 0)  //没有插头了.
                return f[now].sum[i];
        return 0;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++)
            scanf("%s",map[i] + 1);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                if (map[i][j] == '.')
                    tx = i,ty = j; //找右下角的非障碍点
        if (!tx)
            puts("0");
        else
            printf("%lld
    ",solve());
    
        return 0;
    }
  • 相关阅读:
    Spring-boot-admin功能说明
    Spring Boot Admin 的使用 2
    Spring Boot Admin的使用
    windows下的Nodejs及npm的安装、常用命令,Nodejs开发环境配置
    dataTables-使用详细说明整理
    JQuery Datatables Dom 和 Language 参数详细说明
    深入SpringBoot:自定义PropertySourceLoader
    java complier compliance level问题引发的思考
    在线代码格式化工具
    Gradle中的buildScript代码块
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8457162.html
Copyright © 2011-2022 走看看