FZU 2093 找兔子 状压DP

题目链接:找兔子

n的范围是[1, 15],可以用0 到 (1<<n)-1 的数表示全部状态,用dp[i] = t表示到达状态i的最少时间是t,对于每个点,如果它能到达的所有点在t秒时都已经确定了会不会有兔纸,那这个点就确定了在(t+1)s会不会有兔纸。对于每个时刻,可以询问<=2个点,所以由初始状态可以搜到最后确定能不能找到兔纸。

我没有想到找不到兔纸的数据...

关键是dp的思想==很巧妙。

附代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#define maxn 16
#define inf 10000000
using namespace std;

int edge[maxn]; // 储存每个点的边的信息
int dp[1<<maxn]; // dp[i]表示到达i状态的最少时间,一共有1<<n个状态 i的二进制中1表示已经确定了该点,0表示还没有

int main() {
    int t;
    cin >> t;
    while(t--) {
        int n, m;
        memset(edge, 0, sizeof(edge));
        cin >> n >> m;
        for (int i=0; i<=(1<<n); ++i) {
            dp[i] = inf;
        }

        for (int i=0; i<m; ++i) {
            int x, y;
            cin >> x >> y;
            x--, y--;
            edge[x] |= (1<<y);
            edge[y] |= (1<<x);
        }

        for (int i=0; i<n; ++i) {
            if (!edge[i]) edge[i] |= (1<<i);
        }

        queue<int>que;
        dp[0] = 0;
        que.push(0);

        while(!que.empty()) {
            int now = que.front();
            que.pop();
            int nxt = 0;

            for (int i=0; i<n; ++i) {
                if ((now&edge[i]) == edge[i]) {
                    nxt |= (1<<i); // 所有当前已经确定的点(不是状态)都保存在了nxt中。
                }
            }

            for (int i=0; i<n; ++i) {
                for (int j=i+1; j<n; ++j) {
                    int nxtstu = (nxt|(1<<i)|(1<<j)); //可以确定状态nxtstu 时间选择min
                    if (dp[nxtstu] > dp[now] + 1) {
                        dp[nxtstu] = dp[now] + 1;
                        que.push(nxtstu);// 如果当前状态更新 加入队列以更新它可以到达的状态
                    }
                }
            }
        }

        int tot = (1<<n)-1;
        int ans = dp[tot];  //全部点都确定的一个状态

        for (int i=0; i<n; ++i) {
            int stu = (tot^(1<<i)); //每个还有一个点没确定的状态
            ans = min(ans, dp[stu]);
        }

        if (ans == inf) {
            cout << "-1\n";
        }
        else cout << ans << endl;
    }
    return 0;
}

  

时间: 2024-10-15 14:31:22

FZU 2093 找兔子 状压DP的相关文章

FZU 1025 状压dp 摆砖块

云峰菌曾经提到过的黄老师过去讲课时的摆砖块 那时百度了一下题目 想了想并没有想好怎么dp 就扔了 这两天想补动态规划知识 就去FZU做专题 然后又碰到了 就认真的想并且去做了 dp思想都在代码注释里 思想是很好想的..唯一的难点大概是 c++里面没有同或这种东西 得自己写 而我又不怎么会位运算 问了蕾姐半天也没搞懂怎么用~这个取反符号 到最后怒而手写了函数 一开始想的是 init后 输入nm都可以秒出 但是在使用~的路途上 发现至少我的方法 做这个题 不能做到init后随便输入 因为 每行 都有

[luoguP3092] [USACO13NOV]没有找零No Change(状压DP + 二分)

传送门 先通过二分预处理出来,每个硬币在每个商品处最多能往后买多少个商品 直接状压DP即可 f[i]就为,所有比状态i少一个硬币j的状态所能达到的最远距离,在加上硬币j在当前位置所能达到的距离,所有的取max 是满足最优解性质的 #include <cstdio> #include <iostream> #include <algorithm> #define N 17 #define max(x, y) ((x) > (y) ? (x) : (y)) int n

HDU 1074 Doing Homework (状压dp)

题意:给你N(<=15)个作业,每个作业有最晚提交时间与需要做的时间,每次只能做一个作业,每个作业超出最晚提交时间一天扣一分 求出扣的最小分数,并输出做作业的顺序.如果有多个最小分数一样的话,则按照作业字典序输出(注意:输入也是按照字典序输入的) 题解:首先想到的是暴力dfs,但是会超时.接着我们看到n最大只有15,因此可以使用状压dp,但是状态不能用位置表示 但我们可以这样:0表示此作业没有做过,1表示已经用过了,接着遍历0->(1<<n)-1贪心(例如:3(011)可以找2(0

POJ 3254 Corn Fields (状压DP,轮廓线DP)

题意: 有一个n*m的矩阵(0<n,m<=12),有部分的格子可种草,有部分不可种,问有多少种不同的种草方案(完全不种也可以算1种,对答案取模后输出)? 思路: 明显的状压DP啦,只是怎样压缩状态?跟轮廓线DP一样,按格子为单位来设计状态,一个状态只需要表示到其上方和左方的格子,所以最多只需要保存min(n,m)个01状态就行了(可以尝试旋转一下矩阵),最多需要12位.用哈希表来做会比较快吧,不用去考虑无效的状态,比如出现相邻两个1. 1 //#include <bits/stdc++.

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) ) ,能否转移需要判断合

HDU 3001 状压DP

有道状压题用了搜索被队友骂还能不能好好训练了,, hdu 3001 经典的状压dp 大概题意..有n个城市 m个道路  成了一个有向图.n<=10: 然后这个人想去旅行.有个超人开始可以把他扔到任意的一个城市..然后他就在城市之间游荡.要满足他要游玩所有的城市..并且.每个城市最多去两次.要求路程最短..如果他不能游完所有的城市,,那么..就输出-1  否则 输出最短距离 如果用搜索...不靠谱  然后用搜索,, 怎么压缩?? 用一个整型数 i 表示他现在的状态..显然一个城市是要用两位..00

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

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

ZOJ1100 状压DP +深搜

记得做过类似于这类题目是可以用组合数学方法来解决的,可惜淡忘了,也找不到了,看了网上的也有人提到过可以用组合公式解决,可是没人做,都是用了状压DP的方法,这个状压很难讲清楚吧,推荐两篇 第一遍大体看看这个:http://blog.csdn.net/crux_d/article/details/2206736 想要具体实现的时候看看他的解析:http://blog.csdn.net/yan_____/article/details/8719748 #include<iostream> #incl

HDU 1565&amp;1569 方格取数系列(状压DP或者最大流)

方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6206    Accepted Submission(s): 1975 Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的