状态压缩/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. 关于SQL性能优化的十条经验

    1.查询的模糊匹配 尽量避免在一个复杂查询里面使用 LIKE '%parm1%'—— 红色标识位置的百分号会导致相关列的索引无法使用,最好不要用. 解决办法: 其实只需要对该脚本略做改进,查询速度便会 ...

  2. 第一周CoreIDRAW课总结

      一. 问:这节课学到了什么知识? 答:这节课我学到了很多知识,作为初学者的我,老师给我讲了CorelDEAW的启动,安装,基础知识和应用与用途. 基础知识 位图:是由无数个像素点构成的图像,又叫点 ...

  3. PHP中的ArrayAccess用法详解

    在Laravel的源码当中,作者多次使用到了PHP SPL中的ArrayAccess接口,那么这个ArrayAccess接口到底有什么作用呢?我会用一个简单的例子跟大家说明. 请看下面的这段代码,Fo ...

  4. [模拟赛] T3 最优序列

    Description 给出一个长度为n(n<=1000)的正整数序列,求一个子序列,使得原序列中任意长度为m的子串中被选出的元素不超过k(k<=m<=10)个,并且选出的元素之和最 ...

  5. Java Arrays 源码 笔记

    Arrays.java是Java中用来操作数组的类.使用这个工具类可以减少平常很多的工作量.了解其实现,可以避免一些错误的用法. 它提供的操作包括: 排序 sort 查找 binarySearch() ...

  6. java大数(BigInteger)

    JAVA之BigInteger 用Java来处理高精度问题,相信对很多ACMer来说都是一件很happy的事,简单易懂.用Java刷了一些题,感觉Java还不错,在处理高精度和进制转换中,调用库函数的 ...

  7. 用Arduino制作一个二维码显示器

    先上图 场景是这样的, 这几天给CS系统做一个微信支付的功能,  但是生成的二维码是在前台的电脑上..不可能让用户跑到前台的电脑上去扫描...然后拿出了N年前买的Arduino 做了一个二维码显示器. ...

  8. Angular开发实践(五):深入解析变化监测

    什么是变化监测 在使用 Angular 进行开发中,我们常用到 Angular 中的绑定--模型到视图的输入绑定.视图到模型的输出绑定以及视图与模型的双向绑定.而这些绑定的值之所以能在视图与模型之间保 ...

  9. 【BootStrap】 布局组件 I

    BootStrap布局组件 I 除了在原生的HTML基础上进行了外观和类别上的改进,BS还包装了很多组件进库中,设计网页时我们可以方便地调用这些组件.下面来简略地介绍一下各种各样的组件 ■ 字体图标 ...

  10. 【Linux】 文本比较工具 diff和cmp

    Linux 文本比较工具 ■ diff命令 diff用于逐行比较两个文本文件,列出其不同之处 diff [option] <file1> <file2> file1和file2 ...