HDU 4285 circuits(回路不能嵌套的插头DP)

34 篇文章 1 订阅

circuits

Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1149    Accepted Submission(s): 393


Problem Description
  Given a map of N * M (2 <= N, M <= 12) , '.' means empty, '*' means walls. You need to build K circuits and no circuits could be nested in another. A circuit is a route connecting adjacent cells in a cell sequence, and also connect the first cell and the last cell. Each cell should be exactly in one circuit. How many ways do we have?

 

Input
  The first line of input has an integer T, number of cases.
  For each case:
  The first line has three integers N M K, as described above.
  Then the following N lines each has M characters, ‘.’ or ‘*’.
 

Output
  For each case output one lines.
  Each line is the answer % 1000000007 to the case.
 

Sample Input
  
  
2 4 4 1 **.. .... .... .... 4 4 1 .... .... .... ....
 

Sample Output
  
  
2 6
 

Source
 

Recommend
liuyiding

题目大意:

    有一个n*m的矩阵,在矩阵上找K条不相交不嵌套的回路,使得每个点被经过恰好一次,求方案数。


解题思路:

    首先在矩阵上找回路,使得每个点被经过一次,显然是一个插头dp,至于题目要求的刚好K条回路,只需要在状态中加一维当前已经包含的回路数即可。为了使回路之间不嵌套需要在没次形成回路的时候判定一下轮廓线左边右边的插头是否为偶数,如果为偶数则可以合并,否则存在嵌套不能合并(画一下图就可以理解)。


AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <vector>
#include <queue>
#include <stack>
#include <deque>
#include <string>
#include <map>
#include <set>
#include <list>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXN=12+3;
const int MOD=1000000007;

struct HashMap
{
    const static int mod=300007;
    const static int maxn=1000010;
    int head[mod];//链表头指针
    int next[maxn];//指向链表下一个节点
    int size;//当前节点数
    LL key[maxn];
    int val[maxn];//键,值
    void clear()
    {
        size=0;
        memset(head,-1,sizeof head);
    }
    inline void insert(LL _key,int _val)
    {
        int p=_key%mod;//取模后对应的链
        for(int i=head[p];~i;i=next[i])
            if(key[i]==_key)
            {
                val[i]=(val[i]+_val)%MOD;
                return ;
            }
        key[size]=_key;
        val[size]=_val;
        next[size]=head[p];
        head[p]=size++;
    }
}hm[2];

int maze[MAXN][MAXN];
int tmp_state[MAXN];
int ch[MAXN];
int num;//圈的个数
int N, M, K;

void decode(int *tmp_state, int m, LL key)
{
    num=key&63;//前6位表示当前状态的环数
    key>>=6;
    for(int i=m;i>=0;--i)
    {
        tmp_state[i]=key&7;
        key>>=3;
    }
}

LL encode(int *tmp_state, int m)//最小表示法
{
    int cnt=1;
    mem(ch, -1);
    ch[0]=0;
    LL key=0;
    for(int i=0;i<=m;++i)
    {
        if(ch[tmp_state[i]]==-1)
            ch[tmp_state[i]]=cnt++;
        tmp_state[i]=ch[tmp_state[i]];
        key<<=3;
        key|=tmp_state[i];
    }
    key<<=6;
    key|=num;
    return key;
}

void dp_blank(int y, int x, bool now)//对可以进入的地区dp
{
    for(int i=0;i<hm[now].size;++i)
    {
        decode(tmp_state, M, hm[now].key[i]);
        int left=tmp_state[x-1], up=tmp_state[x];
        if(left && up)
        {
            if(left==up)
            { 
                if(num>=K)
                    continue;
                int t=0;//为了避免环嵌套的情况,两边的插头数必须为偶数
                for(int p=0;p<x-1;++p)
                    if(tmp_state[p])
                        ++t;
                if(t&1)
                    continue;
                ++num;
                tmp_state[x-1]=tmp_state[x]=0;
                hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);
            }
            else
            {
                tmp_state[x-1]=tmp_state[x]=0;
                for(int i=0;i<=M;++i)
                    if(tmp_state[i]==up)
                        tmp_state[i]=left;
                hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);
            }
        }
        else if(left || up)
        {
            int t=left?left:up;
            if(maze[y][x+1])
            {
                tmp_state[x-1]=0;
                tmp_state[x]=t;
                hm[!now].insert(encode(tmp_state, M), hm[now].val[i]);
            }
            if(maze[y+1][x])
            {
                tmp_state[x-1]=t;
                tmp_state[x]=0;
                hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);
            }
        }
        else
        {
            if(maze[y][x+1] && maze[y+1][x])
            {
                tmp_state[x-1]=tmp_state[x]=13;
                hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);
            }
        }
    }
}

void dp_wall(int y, int x, bool now)//对不可进入的地区dp
{
    for(int i=0;i<hm[now].size;++i)
    {
        decode(tmp_state, M, hm[now].key[i]);
        hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);
    }
}

void solve()
{
    bool now=0;
    hm[now].clear();
    hm[now].insert(0, 1);
    for(int i=1;i<=N;++i)
        for(int j=1;j<=M;++j)
        {
            hm[!now].clear();
            if(maze[i][j])
                dp_blank(i, j, now);
            else dp_wall(i, j, now);
            now^=1;
        }
    int ans=0;
    for(int i=0;i<hm[now].size;++i)
        if(hm[now].key[i]==K)
            ans=(ans+hm[now].val[i])%MOD;
    printf("%d\n", ans);
}

int main()
{
    int T_T;
    scanf("%d", &T_T);
    while(T_T--)
    {
        scanf("%d%d%d", &N, &M, &K);
        mem(maze, 0);
        for(int i=1;i<=N;++i)
        {
            char s[MAXN];
            scanf("%s", s);
            for(int j=1;j<=M;++j)
                if(s[j-1]=='.')
                    maze[i][j]=1;
        }
        solve();
    }
    
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值