• [CF11D]A Simple Task 题解


    题解

    我们从最简单的思路开始考虑,首先看到题目发现(n)非常小,于是很容易想到状态压缩。
    我们考虑比较直觉的状态,f[i][j][k]表示以i为起点,当前在j,之前去过的点状态为k的简单环的方案数。
    仔细思考以后,我们发现这个状态有问题,问题在于,每一个环再环内的每个点都被计算了一次。
    那么怎么避免这种状态呢?我们考虑让每个环仅由其中编号最小的点贡献。
    这样操作了以后,每个环仍然被计算了两遍(顺时针一次,逆时针一次),但是关系不大,最后统计答案的时候除2即可。
    考虑到起点已经是环中最小的点了,于是我们无需再状态里额外记录它。
    于是将状态变换成f[i][j]表示当前在i,之前去过的点状态为j的简单环的方案数。
    那么状态之间如何转移呢?直接DP有些困难,于是我们使用记忆化搜索。
    在记忆化搜索的时候要记录一个值表示当前去过几个点,因为显然数量在2即以下的点构不成简单环,但会被记搜记录答案,特判即可。

    代码

    #include <cstdio>
    
    namespace fast_IO{
        const int IN_LEN = 10000000, OUT_LEN = 10000000;
        char ibuf[IN_LEN], obuf[OUT_LEN], *ih = ibuf + IN_LEN, *oh = obuf, *lastin = ibuf + IN_LEN, *lastout = obuf + OUT_LEN - 1;
        inline char getchar_(){return (ih == lastin) && (lastin = (ih = ibuf) + fread(ibuf, 1, IN_LEN, stdin), ih == lastin) ? EOF : *ih++;}
        inline void putchar_(const char x){if(oh == lastout) fwrite(obuf, 1, oh - obuf, stdout), oh = obuf; *oh ++= x;}
        inline void flush(){fwrite(obuf, 1, oh - obuf, stdout);}
        int read(){
            int x = 0; int zf = 1; char ch = ' ';
            while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar_();
            if (ch == '-') zf = -1, ch = getchar_();
            while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar_(); return x * zf;
        }
        void write(long long x){
            if (x < 0) putchar_('-'), x = -x;
            if (x > 9) write(x / 10);
            putchar_(x % 10 + '0');
        }
    }
    
    using namespace fast_IO;
    
    struct Edge{
    	int to, next;
    } edges[1005];
    
    int head[20], edge_num = 0;
    
    inline void addEdge(int from, int to){
    	edges[++edge_num] = (Edge){to, head[from]};
    	head[from] = edge_num;
    }
    
    long long f[20][1048577];
    int vis[20];
    
    long long DFS(int frt, int u, int sta, int cnt){
    	vis[u] = 1;
    	if (f[u][sta])
    		return f[u][sta];
    	for (int c_e = head[u]; c_e; c_e = edges[c_e].next){
    		int v = edges[c_e].to;
    		if ((1 << (v - 1)) & sta){
    			if (cnt > 2 && v == frt)
    				++f[u][sta];
    		}
    		else
    			if (v > frt)
    				f[u][sta] += DFS(frt, v, sta | (1 << (v - 1)), cnt + 1);
    	}
    	return f[u][sta];
    }
    
    int main(){
    	int n = read(), m = read();
    	for (int i = 0; i < m; ++i){
    		int u = read(), v = read();
    		addEdge(u, v), addEdge(v, u);
    	}
    	long long ans = 0;
    	for (int i = 1; i <= n; ++i)
    		ans += DFS(i, i, (1 << (i - 1)), 1);
    	write(ans / 2); flush();
    	return 0;
    }
    
  • 相关阅读:
    如何理解 Dictionary Entity Type的作用 !
    Compiere去掉参考下的"显示翻译"
    Compiere去掉菜单中的"脚本""编辑器"
    Compiere去掉选择中的选择下的"用户界面设置""角色"两个按钮
    改变compiere界面
    Compiere a script to remove a client
    Compiere 制造模块
    Compiere去掉启动时的下面显示的进度条
    Compiere修改数据库连接对话窗的图像
    NHibernate Contrib Best Practice
  • 原文地址:https://www.cnblogs.com/linzhengmin/p/11211754.html
Copyright © 2020-2023  润新知