状态压缩/Bitmask

在动态规划问题中,我们会遇到需要记录一个节点是否被占用/是否到达过的情况。而对于一个节点数有多个甚至十几个的问题,开一个巨型的[0/1]数组显然不现实。于是就引入了状态压缩,用一个整数的不同二进制位来表示该节点的状态。

Description

  • Given a simple graph, output the number of simple cycles in it. A simple cycle is a cycle with no repeated vertices or edges.

Input&Output

Input

  • The first line of input contains two integers n and m (1 ≤ n ≤ 19, 0 ≤ m) – respectively the number of vertices and edges of the graph. Each of the subsequent m lines contains two integers a and b, (1 ≤ a, b ≤ n, a ≠ b) indicating that vertices a and b are connected by an undirected edge. There is no more than one edge connecting any pair of vertices.

Output

  • Output the number of cycles in the given graph.

Sample

Input

4 6
1 2
1 3
1 4
2 3
2 4
3 4

Output

7

Solution

  • 大意是求简单无向图的环数,暴搜遍历必然会TLE,重复环的处理也十分复杂。
  • 考虑状态压缩,用二进制位来表示当前状态是否经过了特定的点。为了减轻重复环的处理难度,我们约定只计算起点序小于当前节点的状态(在代码中会有解释)。若节点i与当前节点y之间有边,状态的转移有以下几种条件:
  1. 若当前状态的起点序大于当前节点 (k&-k>(1<<y)) ,不转移。
  2. 若当前状态经过了当前节点 (k&(1<<y)) ,判断起点是否就是当前节点,若是,意味着我们找到了环,更新答案。
  3. 若当前状态没有经过当前节点,则更新经过当前节点的状态 f[k|(1<<y)][y] ,由 f[k][i] 贡献。
  • 遍历以每个节点为起点的所有状态,我们可以得到一个ans。但需要注意的是,这种计算方式会将两点间连一条边的路径(为什么?)和一个环的双向都计算在内,输出时需要将答案减去边数再除以2.
    细节与边界处理
  • 由于二进制位需要从第0位开始,我们不妨在建图时同一将点的编号减1,方便计算。节点的遍历也要从0到n-1。
  • 初始状态下,以节点i为起点,只经过i的状态,f值为1。
  • 代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 20
    #define maxe 400
    using namespace std;
    typedef long long ll;
    struct edge{
    int to,nxt;
    }e[maxe];
    int n,m,x,y,edgenum,lnk[maxn];
    ll ans,f[1<<maxn][maxn];
    void add(int bgn,int end)//事实上,节点比较少,邻接矩阵也可以存下
    {
    edgenum++;
    e[edgenum].to=end;
    e[edgenum].nxt=lnk[bgn];
    lnk[bgn]=edgenum;
    }
    int main()
    {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        add(x-1,y-1);
        add(y-1,x-1);
    }
    for(int i=0;i<n;++i)f[1<<i][i]=1;
    for(int k=0;k<(1<<n);++k){
        for(int i=0;i<n;++i){
            if(!f[k][i])continue;
            for(int p=lnk[i];p;p=e[p].nxt){
                int y=e[p].to;
                if((k&-k)>(1<<y))continue;//判断起点序
                if(k&(1<<y)){
                    if((k&-k)==(1<<y))//判断环
                        ans+=f[k][i];
                }
                else f[k|(1<<y)][y]+=f[k][i];
            }
        }
    }
    ans=(ans-m)/2;
    printf("%I64d",ans);
    return 0;
    }

[CodeForces 11D] A Simple Task - 状态压缩入门的更多相关文章

  1. CodeForces - 11D A Simple Task

    Discription Given a simple graph, output the number of simple cycles in it. A simple cycle is a cycl ...

  2. Codeforces 11D A Simple Task 统计简单无向图中环的个数(非原创)

    太难了,学不会.看了两天都会背了,但是感觉题目稍微变下就不会了.dp还是摸不到路子. 附ac代码: 1 #include<iostream> 2 #include<cstdio> ...

  3. Codeforces C. A Simple Task(状态压缩dp)

    题目描述:  A Simple Task time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  4. 计数排序 + 线段树优化 --- Codeforces 558E : A Simple Task

    E. A Simple Task Problem's Link: http://codeforces.com/problemset/problem/558/E Mean: 给定一个字符串,有q次操作, ...

  5. Codeforces 558E A Simple Task (计数排序&&线段树优化)

    题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memor ...

  6. Codeforces 558E A Simple Task(权值线段树)

    题目链接  A Simple Task 题意  给出一个小写字母序列和若干操作.每个操作为对给定区间进行升序排序或降序排序. 考虑权值线段树. 建立26棵权值线段树.每次操作的时候先把26棵线段树上的 ...

  7. Codeforces 580D Kefa and Dishes(状态压缩DP)

    题目链接:http://codeforces.com/problemset/problem/580/D 题目大意:有n盘菜每个菜都有一个满意度,k个规则,每个规则由x y c组成,表示如果再y之前吃x ...

  8. CodeForces 588E A Simple Task(线段树)

    This task is very simple. Given a string S of length n and q queries each query is on the format i j ...

  9. Codeforces J. A Simple Task(多棵线段树)

    题目描述: Description This task is very simple. Given a string S of length n and q queries each query is ...

随机推荐

  1. python笔记之类

    类 python不直接支持私有方式,可以在方法或者属性之前加上双下划线,将其变为私有,即外部无法直接调用 访问私有方法或者属性,方法是: _<类名><变量名> 首先类定义 # ...

  2. 什么是IPFS?(一)

    写在前面: 今天先写到这里, 关于IPFS的所有事情小编都想快点告诉大家, 但毕竟精力有限, 小编尽量抽出时间提供更多的关于IPFS的信息. ----------------------------- ...

  3. 【吐槽向】iOS 中的仿射变换

    什么是仿射变换矩阵 CGAffineTransform 实际上就是一个用于绘制 2D 图形的的仿射变换矩阵.仿射变换矩阵用于旋转.缩放.平移.扭曲(skew)在图形上下文中绘制的对象.CGAffine ...

  4. es6学习笔记--新数据结构Set,Map以及WeakSet,WeakMap

    在javascript中,存储数据的方式大部分就是以数组或者对象形式存储的,es6出现了4种新集合Set,Map,WeakSet,WeakMap来存储数据,简化了编程. 集合--Set 类似于数组,但 ...

  5. 背景新增属性和css渐变及倒影

    背景新增属性和css渐变及倒影 一.background新增属性 background-size:指定对象的背景图像的尺寸大小. background:url() 0 0,url() 0 100%;多 ...

  6. poj 2503 查字典

    Description You have just moved from Waterloo to a big city. The people here speak an incomprehensib ...

  7. ES6 中 Promise 详解

    Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Promise 提供统一的 API ...

  8. echarts地图的引用

    最近是跟echarts杠上了 所在公司是搞数据的 所以身为前端的我 就必须使用echarts将数据展示出来 ,进公司一周 ,前前后后大概用了八九种echarts图,我举得最难的就是引用的地图,因为刚开 ...

  9. php中heredoc与nowdoc的使用方法

    一.heredoc结构及用法 Heredoc 结构就象是没有使用双引号的双引号字符串,这就是说在 heredoc 结构中单引号不用被转义.其结构中的变量将被替换,但在 heredoc 结构中含有复杂的 ...

  10. 关于php日期前置是否有0

    例如:2018-01-04,这个日期和月份前置是有0 如果不想有0,date( 'y-n-j',time() ):默认的是date( 'y-m-d',time() ),这个日期和月份前置是有0. da ...