zoukankan      html  css  js  c++  java
  • ZOJ 3256 Tour in the Castle

    ZOJ_3256

        这个题目在我最早学插头dp的时候就开始折磨我了,当时没能拿下这个题,主要是当时有两点没弄懂:①对于表示一个图的邻接矩阵的n次方,其中(i,j)位置的元素表示点i经n步到达点j的方案数;②一开始学插头dp就都是逐格进行dp的,而这个要先逐列dp,待拓展出所有可能的列的状态后再进行矩阵乘法,所以顿时不知道怎么做了。

        其实在逐列dp的时候可以先2^n枚举出当前列中每个位置有无右插头,同时再考虑上左边前一列右插头的状态(这里的状态是指:有无插头,以及是哪个连通块的插头),然后自上向下扫一遍,由于相邻的两个插头(两个插头可能都是前一列的,可能都是当前列的,也可能各一个)必然要形成通路(上下插头的有无在左右插头的有无确定之后就确定了,所以就不用管上下插头的有无了),这样在扫的过程中就可以判定两列状态是否可以转移了,同时也可以根据插头合并的情况递推出当前列右插头完整的状态(前面只是枚举了有无,这里就可以推算出是属于哪个连通块了),如果是新建了一个连通块就新开一个数表示这个连通块。递推完成后,为了减少状态的重复,再将递推出来的状态用一下最小表示法就可以了。

        此外我是用4进制表示的插头的状态,在实际初始化的时候就会发现即便N=7时拓展出来的可能的列的状态也不足120个。

    #include<stdio.h>
    #include<string.h>
    #define HASH 419
    #define MAXD 1010
    #define MOD 7777777
    typedef long long LL;
    int N, M, D, g[128][128], code[10], h[10];
    struct Matrix
    {
        int a[128][128];
        Matrix operator * (const Matrix &t) const
        {
            int i, j, k;
            LL sum;
            Matrix ans;
            for(i = 0; i < D; i ++)
                for(j = 0; j < D; j ++)
                {
                    sum = 0;
                    for(k = 0; k < D; k ++) sum += (LL)a[i][k] * t.a[k][j];
                    ans.a[i][j] = sum % MOD;
                }
            return ans;
        }    
    }mat, unit;
    struct HashMap
    {
        int head[HASH], size, next[MAXD], st[MAXD];
        void init()
        {
            memset(head, -1, sizeof(head)), size = 0;    
        }
        int push(int _st)
        {
            int i, h = _st % HASH;
            for(i = head[h]; i != -1; i = next[i])
                if(st[i] == _st) break;
            if(i == -1)
            {
                i = size;
                st[size] = _st;
                next[size] = head[h], head[h] = size ++;
            }
            return i;
        }
    }hm;
    int encode(int *code, int n)
    {
        int i, st = 0, cnt = 0;
        memset(h, -1, sizeof(h)), h[0] = 0;
        for(i = 0; i < n; i ++)
        {
            if(h[code[i]] == -1) h[code[i]] = ++ cnt;
            st = st << 2 | h[code[i]];
        }
        return st;
    }
    void decode(int *code, int n, int st)
    {
        for(int i = n - 1; i >= 0; i --) code[i] = st & 3, st >>= 2;    
    }
    int trans(int x, int y)
    {
        for(int i = 0; i < N; i ++) if(code[i] == x) code[i] = y;
    }
    int check(int st, int nst)
    {
        int i, j, k, flag = 0, cnt = 0;
        decode(code, N, st);
        int t[10];
        memcpy(t, code, sizeof(code));
        for(i = 0; i < N; i ++)
        {
            if(flag == 0)
            {
                if(code[i] == 0 && (nst & 1 << i) == 0) return 0;
                if(code[i] && (nst & 1 << i)) continue;
                if(code[i]) flag = code[i];
                else flag = -1;
                k = i;
            }
            else
            {
                if(code[i] && (nst & 1 << i)) return 0;
                if(code[i] == 0 && (nst & 1 << i) == 0) continue;
                if(code[i])
                {
                    if(code[i] == flag && (nst != 0 || i != N - 1)) return 0;
                    if(flag > 0) trans(code[i], code[k]), code[i] = code[k] = 0;
                    else code[k] = code[i], code[i] = 0;
                }
                else
                {
                    if(flag > 0) code[i] = code[k], code[k] = 0;
                    else code[i] = code[k] = N + (cnt ++);
                }
                flag = 0;
            }
        }
        if(flag != 0) return 0;
        return 1;
    }
    void init()
    {
        int i, j, k, st, nst;
        hm.init();
        memset(code, 0, sizeof(code)), code[0] = code[N - 1] = 1;
        hm.push(0), hm.push(encode(code, N));
        memset(g, 0, sizeof(g));
        for(i = 1; i < hm.size; i ++)
        {
            st = hm.st[i];
            for(nst = 0; nst < (1 << N); nst ++)
                if(check(st, nst))
                {
                    j = hm.push(encode(code, N));
                    g[i][j] = 1;
                }
        }
        D = hm.size;
    }
    void powmod(int n)
    {
        while(n)
        {
            if(n & 1) mat = mat * unit;
            unit = unit * unit, n >>= 1;    
        }    
    }
    void solve()
    {
        int i, j;
        memset(mat.a, 0, sizeof(mat.a));
        for(i = 0; i < D; i ++) mat.a[i][i] = 1;
        memcpy(unit.a, g, sizeof(g));
        powmod(M);
        if(mat.a[1][0] == 0) printf("Impossible\n");
        else printf("%d\n", mat.a[1][0]);
    }
    int main()
    {
        while(scanf("%d%d", &N, &M) == 2)
        {
            init();
            solve();    
        }
        return 0;    
    }
  • 相关阅读:
    XCode5中新建工程后强制使用了ARC,如何去掉?
    面向对象程序的设计原则--Head First 设计模式笔记
    ios控件自定义指引
    iOS UITableViewDelegate && UITableViewDataSource 执行顺序
    awakeFromNib方法和viewDidLoad方法区别
    ios 视图的旋转及应用
    线段树模板 (刘汝佳)
    poj 3468
    hdu 2829(四边形优化 && 枚举最后一个放炸弹的地方)
    poj 3517(约瑟夫环问题)
  • 原文地址:https://www.cnblogs.com/staginner/p/2684712.html
Copyright © 2011-2022 走看看