复杂状态的动态规划

紫皮各种……

1.最优配对问题

d(i,S) = min{|Pi,Pj| + d(i-1,S-i-j) | j在S中};

空间 n 个点,配成n/2对使两点的距离之和尽量少

for(int i = 1;i <= n;i++){
        for(int S = 0;S < (1 << n);S++){
            d[i][S] = INF;
            for(int j = 0;j < n;j++){
                if(S & (1<< j)){ /// 如果j能参与配对
                    d[i][S] = min(d[i][S],dist(i,j) + d[i-1][S^(1<<i)^(1<<j)]);
                }
            }
        }
    }

其中那个i隐含在S中所以可以转换为

int getMax(int x){
    for(int i = 31;i>=0;i--){
        int m = (x>>i)&1;
        if(m){
            return i+1;
        }
    }
    return 0;
}
for(int S = 0;S < (1<<n);S++){
        int i = getMax(S);
        d[S] = INF;
        for(int j = 0;j < n;j++){
            d[S] = min(d[S],dist(i,j) + d[i-1][s^(1<<i)^(1<<j)]);
        }
    }

2.货郎担问题

设起点城市和终点城市为0用d(i , S)表示从当前城市i 到 S的集合中城市各一次回到城里0的长度

则 d(i,S) = min{d(j,S-j) + dist(i,j) | j在S中};

3.图的色数问题

一个无相图,相邻的的颜色不能相同,求最小的颜色数

d(S) 表示把结点集S染色,所需要颜色数的最小值,则d(S) = d(S-‘S‘) + 1;‘S‘为S的子集且不存在‘S‘内两个结点u,v两个相邻 ,‘S‘表示可以染成同一颜色的点集

通过预处理判断是够可以染成同一个颜色即内部没有边

for(int S = 1;S < (1<<n);S++){
        d[S] = INF;
        for(int S0 = S; S0;S0 = (S0-1)&S){
            if(no_edges[S0]){
                d[S] = min(d[S],d[S-S0]+1);
            }
        }
    }

uva 10817 校长的烦恼(代码没提交,233333)

m个教师n个求职者,需讲授s个课程,已知每人工资,求支付最小工资使每门课至少有两名教师能教

集合s1表示恰好只有一个人能教的课程,s2多数人教,s0每人教,d(i,s1,s2)表示要前i该状态的最小花费 则转移方程为d(i,s1,s2) = min{d(i+1,s1‘,s2‘)+c[i],d(i,s1,s2)} 第i个选择聘请或不聘请,前m个是教师,不能解雇,所以前m项只能选择后者

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <vector>

const int MAXN = 120 + 10;
const int INF = 0xffffff;
const int mod = 1000007;
const double Pi = atan(1.0)*4;
const double ESP = 10e-8;

using namespace std;

int m,n,s,c[MAXN],st[MAXN],d[MAXN][1<<10][1<<10];

int dp(int i,int s0,int s1,int s2){ ///s0表示谁都没有教,s1表示恰好有一个人能教的科目集合,s2表示可以多人教的科目集合
    if(i == m+n) ///表示最后决策,如果如果s2能够教的科目如果都能教则选择,否则不选
        return s2 == (1<<s)-1?0:INF;
    int & ans = d[i][s1][s2];
    if(ans >= 0){ ///记忆化搜索
        return ans;
    }
    ans = INF;
    if(i >= m){ ///前i个决策只能选老师,不存在不选择的情况
        ans = dp(i+1,s0,s1,s2); //不选
    }
    int m0 = st[i]&s0;  ///st[i]与s0的交集
    int m1 = st[i] & s1; ///交集
    s0 ^= m0; ///把第i个老师能教的课程从s0剔除
    s1 = (s1^m1)|m0; ///把第i个老师能教的科目从s1中剔除之后取出在s0中还有的交集
    s2 |= m1;///s2增加了能教的科目
    ans = min(ans,c[i]+dp(i+1,s0,s1,s2)); //选
    return ans;
}

int main(){
    //freopen("input.txt","r",stdin);
    while(~scanf("%d%d%d",&s,&m,&n)){
        if(!s){
            break;
        }
        memset(st,0,sizeof(st));
        memset(d,0,sizeof(d));
        for(int i = 0;i < m+n;i++){
            scanf("%d",&c[i]);
            char ch;
            int x;
            ch = getchar();
            while(ch != 10){
                scanf("%d",&x);
                ch = getchar();
                x--;
                st[i] += (1<<x);
            }
        }
        printf("%d\n",dp(0,(1<<s)-1,0,0));
    }
    return 0;
}

poj 3809

n(n <=128)个物体,m个特征(m <= 11),求最小询问次数把n个物体区分开

d(s,a) 表示询问过了特征集合s,确认w具有特征a的询问次数

则 下一次为 max{d(s+{k},a+{k}),d(s+{k},a)} + 1

考虑所有的k取最小值……

如果能区分的话就返回……

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <stack>
#include <cctype>
#include <string>
#include <queue>
#include <map>
#include <set>

using namespace std;

const int INF = 0x7ffffff;
const double ESP = 10e-8;
const double Pi = 4 * atan(1.0);
const int MAXN =   11 + 10;
const long long MOD =  1000000007;
const int dr[] = {1,0,-1,0,-1,1,-1,1};
const int dc[] = {0,1,0,-1,1,-1,-1,1};
typedef long long LL;
int d[1<<12][1<<12];
int p[1<<12];
int n,m;
int dfs(int s,int a){
    if(d[s][a] != INF){ ///如果访问过了
        return d[s][a];
    }
    int cnt = 0;
    for(int i = 0;i < n;i++){ ///统计当前特征,如果被询问过的特征中p可以被确定的个数
        if((p[i]&s) == a){
            cnt++;
        }
    }
    if(cnt <= 1){ ///如果只剩下一个或没有则就不需要进行继续询问则返回0
        d[s][a] = 0;
        return d[s][a];
    }
    for(int i = 0;i < m;i++){ ///如果被确定的个数超过了1个则需要继续询问
        if(s & (1 << i)){ ///如果第i个特征被询问过了就不需要询问
            continue;
        }
        int nn = s|(1<<i); ///询问第i个特征
        int & dd = d[s][a];
        dd = min(dd,max(dfs(nn,a),dfs(nn,a^(1<<i)))+1); ///询问i,和不询问i的最小值
    }
    return d[s][a];
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("inpt.txt","r",stdin);
   // freopen("output.txt","w",stdout);
#endif
    while(~scanf("%d%d",&m,&n)){
        if(!m && !n){
            break;
        }
        memset(p,0,sizeof(p));
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++){
                int tmp;
                scanf("%1d",&tmp);
                if(tmp)
                    p[i] |= 1<<j;
            }
        }
        for(int i = 0;i < (1<<11)+10;i++){
            for(int j = 0;j <(1<<11)+10;j++){
                d[i][j] = INF;
            }
        }
        printf("%d\n",dfs(0,0));
    }
    return 0;
}

嗨嗨嗨……数位dp总算会一点了……

开始数论初步了……

自己还是太差……

时间: 2024-08-03 05:56:15

复杂状态的动态规划的相关文章

奇妙的算法—状态压缩动态规划

华电北风吹 天津大学认知计算与应用重点实验室 日期:2015/8/27 由于代码未调试完全正确论文草稿呈现 poj上一道需要用到状态压缩动态规划,链接http://poj.org/problem?id=3254 网上看到有很多人写出了代码,参考了一个带备忘的自顶向下的动态规划解法,自己写了一个有底向上的动态规划解法 有底向上: #include<iostream> #include<math.h> #include<ostream> #include<fstrea

状态压缩动态规划总结

状态压缩是一个很广的概念,在OI中也有很多的应用,当我们把他应用到动态规划中,可以用来精简状态,节约空间,也方便转移.最常见的就是用二进制来表是状态,利用各种位移运算,就可以实现\(O(1)\)的转移.状压DP适用于“窄棋盘”上的DP,否则状态太多无法存下. POJ1185 炮兵阵地 题意:给一个\(N \times M\)的地形盘,有平原和山坡,要求在平原上部署尽量多的炮(攻击范围2),使得不互相攻击. 数据范围:N <= 100:M <= 10,符合条件.如何表示状态?按行DP,一个二进制

状态压缩动态规划 -- 炮兵阵地

司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队,一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击不到.从图上可见炮兵的攻击

状态压缩动态规划 -- 棋盘问题 POJ 1321

一个 N * N 的棋盘上面,有些格子不能放,放置 M 的棋子, 每两个棋子不能在同一行或者同一列,问有多少种放法 DFS太慢,用SCR好点点 Python 只有 22 行,其实可以更短,但是得排成很长很长的一行 while True: table = [ [ 0 for j in range( 300 ) ] for i in range( 12 ) ] table[0][0] = 1 boardsize, chessnum = map( int, raw_input().split() )

状态压缩动态规划 -- 旅行商问题

旅行商问题: N个点(N<16)的带权有向图D,求一条路径,使得这条路经过每个点恰好一次, 并且路径上边的权值和最小(或者最大),或者求一条具有这样性质的回路. 状态压缩: 将二进制表示十进制数N的点集,比如: 10 = 0000000000001010 代表第1和3个点已经路过 18 = 0000000000010010 代表第1和4个点已经路过 一个整数就是一个点集, dp_arr[binary][to_]代表经过点集 binary 中,当前终点为to_, 且路径最短的值,若该状态不存在就是

状态压缩动态规划 -- 多米诺骨牌

用1*2 的骨牌通过组合拼成 m * n 的大矩形,问有几种拼法. 题目链接:http://poj.org/problem?id=2411 状态转移: 1.由于上一行的该列竖直放置骨牌为 0,影响到当前行的该列,当前行的该列为 1 2.当前行骨牌横放,上一行骨牌横放, 都为11 3.上一行该列置为 1,当前行当前列立着放为 0 #include <iostream> #include <cstring> using namespace std; #define MAXSIZE 12

状态压缩-动态规划

状态压缩-动态规划 ---By蒟蒻鱼 用二进制表示城市的到达(规划的)状态 每一个二进制数都代表一个唯一的十进制数 预备知识 位运算 优先级 ~ << 和 >> & ^ | 按位与 & 全一则一,否则为零 按位或 | 有一则一,否则为零 按位取反 ~ 是零则一,是一为零 按位异或 ^ 不同则一,相同为零 移位 >> << 基本运算: 集合取并 A|B 集合取交 A&B 集合相减 A&~B 集合相减 ALLBITS~A 置位 A

CODEVS_2800 送外卖 状态压缩+动态规划

原题链接:http://codevs.cn/problem/2800/ 题目描述 Description 有一个送外卖的,他手上有n份订单,他要把n份东西,分别送达n个不同的客户的手上.n个不同的客户分别在1~n个编号的城市中.送外卖的从0号城市出发,然后n个城市都要走一次(一个城市可以走多次),最后还要回到0点(他的单位),请问最短时间是多少.现在已知任意两个城市的直接通路的时间. 输入描述 Input Description 第一行一个正整数n (1<=n<=15) 接下来是一个(n+1)

【BZOJ1087】【SCOI2005】互不侵犯King 状态压缩 动态规划 水题 都不用加特技

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44022265"); } 题解: 一开始让我写这道题,其实我是,是接受的. BalaBala. 毕竟水题,都不用特技. 裸状压DP. 直接f[i][j][k]表示第i行状态时j,有k个落子时的方案数. 代码: #include <

[HNOI2012][BZOJ2734] 集合选数|状态压缩动态规划|思路题

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 708  Solved: 414[Submit][Status][Discuss] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤