状压DP
集合状压DP
Doing Homework HDU - 1074
题意:
1:每次给n个作业及其花费时间和截至时间
2:每次做一个作业直到做完才可以换下一个作业
3:对于每一个作业,每超过一天就会减去1分
4:问减去的最小的分数
5:题中的输入顺序是按字典序的
思路:
状态压缩动态规划
在二进制情况下,第 i 位为 1 表示已做完 为 0 表示未做
这样可以用最多 的数来表示所有情况
过程:
用 表示当前处于 状态,循环所有的状态:
- 每次循环判断其他科目是否已完成,
如果未完成,将现在状态加上完成的状态记录为新状态。
注意:
因为需要输出路径,所以dp要记录上一个节点:pre
因为要计算答案,所以dp要记录完成这些科目需要多长时间:time
因为要统计数量,所以dp要记录罚时:num
所有科目均为 1 就是答案,也就是
由于输出是逆序,需要用栈等结构倒过来输出
代码
#include <bits/stdc++.h> using namespace std; const int N = 1<<16; // #define int long long int n; struct node{ string name; int time; int en; }a[20]; struct DP{ int num; int day; int pre; }dp[N]; int GetChange(int num1,int num2) { int n = num1 ^ num2; int i = 0; while(n) { if(n & 1) return i; n >>= 1; i++; } return 0; } void solve(){ cin>>n; for(int i=0;i<n;i++){ cin>>a[i].name>>a[i].en>>a[i].time; } memset(dp,0x3f,sizeof dp); dp[0].num = dp[0].day = dp[0].pre =0; for(int i=0;i<(1<<n);i++){ for(int j=0;j<n;j++){ if((1<<j) & i ) continue; int ne=i|(1<<j); int cnt=max(0 , dp[i].day+a[j].time-a[j].en); // cout<<i<<" "<<j<<" " <<ne<<" "<<cnt<<" "<<"XxX"<<endl; if(dp[ne].num>dp[i].num+cnt){ dp[ne].num=dp[i].num+cnt; dp[ne].day=dp[i].day+a[j].time; dp[ne].pre=i; } } } int k = (1 << n) - 1; cout<<dp[k].num<<endl; stack<int > stk; while(k){ int pos = GetChange(k,dp[k].pre); stk.push(pos); k = dp[k].pre; } while(!stk.empty()) { cout << a[stk.top()].name << endl; stk.pop(); } } signed main() { cin.tie(0); std::cin.sync_with_stdio(false); int t;cin>>t; while(t--) solve(); return 0; }
棋盘状压DP
原文
AcWing 1064. 小国王【线性状压DP+滚动数组优化+目标状态优化】
题目描述
在 的棋盘上放 个国王
国王可攻击相邻的 88 个格子,求使它们 无法互相攻击 的 方案总数
八相邻:
通常意义上的八相邻指的是当前元素的上、下、左、右、左上、右上、左下、右下八个方向
分析
这种 棋盘放置类 问题,在没有事先知道一些特定 性质 的情况下来做,都会想到 爆搜
本题的数据规模,也是向着 爆搜 去设置的
如果我们直接 爆搜,则 时间复杂度 为 是会超时的,因此会想到用 记忆化搜索 来进行优化
考虑一下如何进行 动态规划
由于在第 层放置国王的行为,受到 层和 层以及 层的状态影响
那么我们就可以规定从上往下枚举的顺序,这样考虑第 层状态时,只需考虑 层的状态即可
于是乎我们可以考虑把层数 作为动态规划的 阶段 进行 线性DP
而第 阶段需要记录的就是前 层放置了的国王数量 ,以及在第 层的 棋盘状态
然后按照题意找到,哪些 棋盘状态 是合法的,哪些 棋盘状态的转移 是合法的即可
代码
#include <iostream> #include <vector> using namespace std; typedef long long LL; const int N = 12, M = 1 << N, C = N * N; int n, m, K; LL f[N][C][M]; int cnt[M]; vector<int> legal_state; vector<int> state_trans[M]; bool check(int state) { return !(state & state >> 1); } int count(int state) { int res = 0; for (int i = 0; i < n; ++ i) res += state >> i & 1; return res; } int main() { cin >> n >> K; //预处理所有合法状态 for (int st = 0; st < 1 << n; ++ st) //检查当前状态是否合法 if (check(st)) legal_state.push_back(st), cnt[st] = count(st); m = legal_state.size(); //预处理所有合法状态的合法转移 for (auto cur_st: legal_state) for (auto to_st: legal_state) if (!(cur_st & to_st) && check(cur_st | to_st))//上下不相邻且纵坐标也不相邻 state_trans[cur_st].push_back(to_st); //动态规划 f[0][0][0] = 1; for (int i = 1; i <= n + 1; ++ i) for (int j = 0; j <= K; ++ j) for (auto &state: legal_state) for (auto &pre_st: state_trans[state]) if (j - cnt[state] >= 0) f[i][j][state] += f[i - 1][j - cnt[state]][pre_st]; //统计目标状态的所有方案数 cout << f[n + 1][K][0] << endl; return 0; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/16750127.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步