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) ) ,能否转移需要判断合法性。这里把课程k的先修课程存在一个int里面,S & prev[k] == prev[k]则表示可以选项k课程。dp数组一开始需要初始化成无穷小。最后只要找dp[(1 << n ) - 1] 。

/*输入真恶心*/

#include <map>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int INF = 999999999;

int n,m;

map<string, int> Map;

int w[20];

int prve[20];

void read() {
    cin >> n >> m;getchar();

    char _a[101];
    string _s, _t;
    int _num;
    int _len;
    for (int i=0; i<n; i++) {
        gets(_a);

        _len = strlen(_a);

        int k = 0;
        while(_a[k] < ‘0‘ || _a[k] > ‘9‘) k++;

        _a[k-1] = ‘\0‘;
        _s = (string) _a;

        _num = 0;
        while (k < _len && _a[k] >= ‘0‘ && _a[k] <= ‘9‘) {
            _num = _num * 10 + _a[k] - ‘0‘;
            k++;
        }

        Map[_s] = i;
        w[i] = _num;
    }

    for (int i=0; i<m; i++) {
        gets(_a);

        int k = 0;
        while (_a[k] != ‘-‘) k++;

        _a[k-1] = ‘\0‘;

        _s = (string) _a;
        _t = (string) (_a + k + 4);

        prve[Map[_t]] |= (1 << Map[_s]);
    }
}

int dp[(1<<18)+5];

void DP () {

    for (int s=0; s<(1<<n); s++) {

        int _num = 0;

        for (int i=0; i<n; i++) {
            _num += ((s >> i) & 1);
        }

        for (int i=0; i<n; i++) {
            int ss = s | (1 << i);

            if (((s >> i & 1) != 0) || ((s & prve[i]) != prve[i])) continue;

            if (_num == 0 && prve[i] == 0) {
                dp[ss] = w[i];
                continue;
            }

            dp[ss] = max(dp[ss], dp[s] + (_num + 1) * w[i]);
        }
    }

    cout << dp[(1<<n)-1] << endl;
}

void init() {
    Map.clear();
    fill(dp, dp + (1 << 18), -INF);
    memset(prve, 0, sizeof(prve));
}

int main () {
    int t;
    cin >> t ;
    while (t--) {
        init();
        read();
        DP();
    }
    return 0;
}

时间: 2024-08-02 19:05:14

Codeforces Gym 100676G Training Camp 状压dp的相关文章

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

题意 求出一个n个点m个边的图,求简单环有多少(没有重复点和边). 思路 这是个不错的题,这个状压dp保存的状态不是直接的环,而是路径的个数.s表示的状态为一条路径,则dp[s][i]表示以s的最小编号为起点,以i为终点的环的个数.那么我们就可以通过枚举状态,枚举状态中的起点和枚举路径外的终点,然后判断终点和起点是否相连来判断是否成环. 代码 #include <stdio.h> #include <string.h> #include <iostream> #incl

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的情况,累加

状压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; #

Codeforces Gym 100610 Problem K. Kitchen Robot 状压DP

Problem K. Kitchen Robot Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100610 Description Robots are becoming more and more popular. They are used nowadays not only in manufacturing plants, but also at home. One programmer wit

2014 Super Training #1 B Fix 状压DP

原题: HDU 3362 http://acm.hdu.edu.cn/showproblem.php?pid=3362 开始准备贪心搞,结果发现太难了,一直都没做出来.后来才知道要用状压DP. 题意:题目给出n(n <= 18)个点的二维坐标,并说明某些点是被固定了的,其余则没固定,要求添加一些边,使得还没被固定的点变成固定的,可见题目中的图形sample. 由于n很小,而且固定点的顺序没有限制,所以需要用状态压缩DP. 注意:1.当一个没固定的点和两个固定了的点连接后,该点就被(间接)固定了(

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

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

Codeforces 580D-Kefa and Dishes(状压DP)

原题链接:http://codeforces.com/problemset/problem/580/D 题意:在n个数字中有顺序地选择m个数字,每个数字对应有个值ai,每取一个数字答案加上ai,并且存在k个关系:x y c,如果x恰好排在y的前面,那么答案再加上ci的值.输出最大值. 思路:状压dp.dp[i][j]中,i是已经选了若干个数的情况,j是最后一个被选取的数,i从选取1个到m个枚举下去,j从第1个数到第n个数进行枚举就能得到答案. AC代码: 1 #include<iostream>

CodeForces 757D Felicity&#39;s Big Secret Revealed(状压DP)

题意:给定一个01串,一个有效的n切割定义如下:一个横杠代表一次切割,第一条横杠前面的01串不算,最后一条横杠后面的01串不算,将两个横杠中的01串转化成十进制数字,假设这些数字的最大值是MAX且这些数字囊括了1-MAX的所有数字,则称为一次有效切割.求2~n+1次有效切割的切法. 思路: 由于题目要求包含所有1-MAXN的数字,且n<=75,所以MAXN<=20.另dp[i][j]表示第i位前面有一个横杆且存在j这个状态,接着从第i位开始枚举到第j位为下一个横杆的位置,设这两段横杆之间的数字