Codeforces 11D - A Simple Task (状压DP)

题意

求出一个n个点m个边的图,求简单环有多少(没有重复点和边)。

思路

这是个不错的题,这个状压dp保存的状态不是直接的环,而是路径的个数。s表示的状态为一条路径,则dp[s][i]表示以s的最小编号为起点,以i为终点的环的个数。那么我们就可以通过枚举状态,枚举状态中的起点和枚举路径外的终点,然后判断终点和起点是否相连来判断是否成环。

代码 

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <list>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define LL long long
#define lowbit(x) ((x)&(-x))
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1|1
#define MP(a, b) make_pair(a, b)
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
const int maxn = 1e5 + 10;
const double eps = 1e-8;
const double PI = acos(-1.0);
typedef pair<int, int> pii;
LL dp[1<<20][20];
bool a[20][20];

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n, m, u, v;
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        cin >> u >> v;
        u--, v--;
        a[u][v] = a[v][u] = 1;
    }
    memset(dp, 0, sizeof(dp));
    for (int i = 0; i < n; i++)
        dp[1<<i][i] = 1;
    LL ans = 0;
    for (int s = 1; s < (1 << n); s++)
    {
        int st = 0;
        for (int i = 0; i < n; i++)
            if (s & (1 << i))
                st = i, i = n;
        for (int e = st; e < n; e++)
            if (s & (1 << e))
                for (int i = st; i < n; i++)
                    if (!(s & (1 << i)) && a[e][i])
                    {
                        dp[s|(1<<i)][i] += dp[s][e];
                        if (a[i][st] && __builtin_popcount(s|(1<<i)) >= 3)
                            ans += dp[s][e];
                    }
    }
    cout << ans / 2 << endl;
    return 0;
}
时间: 2024-10-22 11:44:40

Codeforces 11D - A Simple Task (状压DP)的相关文章

CF11D A Simple Task 状压DP

传送门 \(N \leq 19\)-- 不难想到一个状压:设\(f_{i,j,k}\)表示开头为\(i\).结尾为\(j\).经过的点数二进制下为\(k\)的简单路总数,贡献答案就看\(i,j\)之间有没有边. 当然,会有一些问题:①路会算重:②\(2^NN^2\)的数组开不下(当然②才是重点),所以考虑优化算法 考虑类似最小环的优化 设\(f_{i,j}\)表示开头为\(log_2lowbit(j)\),结尾为\(i\),经过的点数二进制下为\(j\)的简单路总数,转移跟上面类似,值得注意的是

Codeforces Gym 100676G Training Camp 状压dp

http://codeforces.com/gym/100676 题目大意是告诉你要修n门课,每门课有一个权值w[i], 在第k天修该课程讲获得k*w[i]的学习点数,给出了课程与先修课程的关系,要修该课程必须修完先修课程.问最多能学到多少点数. 非常简单的一道状压dp(一开始我还误导队友写成两维的去了 T^T); dp[s] : s 的二进制存放的是已经选择的课程,在该状态下的能获得的最大的点数. 这时如果再学一门课程k,将转移到状态ss (s | (1 << k) ) ,能否转移需要判断合

Codeforces 482C. Game with Strings 状压DP

很好的状压dp题目 d[mask]=x   在询问了mask位的情况下,有x状态的串还是不能区分 /// 预处理不好会TLE dp[x]  从一次也没有问,到问到状态x时的概率 可以看 http://blog.csdn.net/houserabbit/article/details/40658791 大神的题解 C. Game with Strings time limit per test 1 second memory limit per test 256 megabytes input s

Codeforces 895C Square Subsets(状压DP 或 异或线性基)

题目链接  Square Subsets 这是白书原题啊 先考虑状压DP的做法 2到70总共19个质数,所以考虑状态压缩. 因为数据范围是70,那么我们统计出2到70的每个数的个数然后从2考虑到70. 设dp[x][mask]为考虑到x这个数的时候,x这个数和之前的所有数中,选出某些数,他们的乘积分解质因数,所有的指数对2取模之后, 状态为mask的方案数. 然后就可以转移了……这个状压DP花了我好几个小时……真是弱啊 哦对最后还要特判1的情况. 每个1选或不选都可以,然后考虑只选1的情况,累加

CodeForces 11D A Simple Task

题目:http://codeforces.com/problemset/problem/11/D 题意:给定一个图,求图中环的数目 为了消除重复计算,我们要从每个点出发,并将这个点作为环的最小点,再次回到出发点时就变成了环 但是这样依然有重复,会有1条边重复走2次的环,同时还会将大于3的环正向跑一遍逆向跑一遍 所以最后答案需要-m再除2 #include<iostream> #include<cstdio> #include<cstring> #include<s

[CodeForces 11D] A Simple Task - 状态压缩入门

状态压缩/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

状压DP题集

[状压DP]Codeforces - 580D Kefa and Dishes (状压DP+记忆化搜索)(经典) 题目大意: 有$n ( n \leq18 )$个菜,现在要挑选$m$道菜,每个菜有一个满意度$a_i$,还有$k$个关系,每个关系为 $a b c$,表示$a$在$b$之前吃的话,就会额外增加$c$的满意度,现在要你输出最大的满意度. 解题分析:$n$的数据范围很小,很容易想到状压DP. #include <bits/stdc++.h> using namespace std; #

CF11D A Simple Task(状压DP)

\(solution:\) 思路大家应该都懂: 状压DP:\(f[i][j]\),其中 \(i\) 这一维是需要状压的,用来记录19个节点每一个是否已经走过(走过为 \(1\) ,没走为 \(0\) ,用 \(2\)进制 压缩一下即可).同时,我们认为状压中已经走过的序号最小的节点为出发节点,\(j\) 即数组第二维是路径终点.(当这两个数相同时,说明找到了一个环). 注:这种方法因为无向图的存在,会出现(同一条路径出现两次)(一条边和两个端点构成非法环)的情况,这只需要在输出答案时 \(ans

CodeForces 21D Traveling Graph 状压dp+欧拉回路

题目链接:点击打开链接 题意: 给定n个点m条边的无向图 求从1点开始经过每条边至少一次最后回到1点的最小路程 显然就是找一条路径可重复的欧拉回路 思路: 首先对于欧拉回路的结论是:所有点的度数都为偶数 因为所有边至少经过一次,那么可以把题意转换成加最少多少条边使得图满足以上结论 而加的边目的是为了把奇度数转成偶度数,先floyd一下得到任意点间加边的最小花费 dp[i]表示状态i下度数都为偶数的最小花费. 状压dp,把i状态下,所有未选择的点中挑2个奇度数的转移即可. #include <cs