状压DP
CF580D
题目链接
https://codeforces.com/problemset/problem/580/D
题目大意

思路
令dp[i][j]表示,吃菜状态为i,且最后一道菜为j的最大满足感!
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 20;
int n, m, t, q;
int a[N],b[N * N][N * N];
ll dp[1 << N][N],ans;
// dp[i][j]表示状态为i时,吃最后一道菜为j时获得的最大满足感!
void solve() {
cin >> n >> m >> q;
for(int i = 0;i < n;++i) {
cin >> a[i];
dp[1 << i][i] = a[i];
}
for(int i = 0;i < q;++i){
int x,y,c;cin >> x >> y >> c;
--x;--y;
b[x][y] = c;
}
for(int i = 0;i < (1 << n);++i){
for(int j = 0;j < n;++j){
// i 不 包 含 j
if((i &(1 << j)) == 0) continue;
for(int k = 0;k < n;++k){
// i 中 不 包 含 k
if(j == k || (!(i &(1 << k)))) continue;
// j 从 k 转移过来
dp[i][j] = max(dp[i][j],dp[i ^ (1 << j)][k] + a[j] + b[k][j]);
}
if(__builtin_popcount(i) == m){
ans = max(ans,dp[i][j]);
}
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
t = 1;
for (int _ = 0; _ < t; _++)
solve();
return 0;
}
CF11D
题目链接
https://codeforces.com/problemset/problem/11/D
题目大意

思路
默认状态i中的序号最小节点为起点,用lowbit表达!
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 20;
int n, m, t;
bool g[N][N];
ll dp[1 << 20][N],ans;
void solve() {
cin >> n >> m;
for(int i = 0;i < m;++i){
int a,b;cin >> a >> b;
--a;--b;
g[a][b] = g[b][a] = true;
}
for(int i = 0;i < n;++i) dp[1 << i][i] = 1;
for(int i = 1;i < (1 << n);++i){
for(int j = 0;j < n;++j){
// i 状 态 下,最 后 一 个 到 达 节 点 j 的 方 案 数
if(!dp[i][j]) continue;
for(int k = 0;k < n;++k){
// 节 点 j 无 法 到 达 节 点 k,或 者 i 状 态 的 起 点 大 于 节 点 k!
if(!g[j][k] || (i & -i) > (1 << k)) continue;
// 如 果 状 态 i 中 有 k 节 点
if((1 << k) & i){
// 首 尾 相 连
if(1 << k == (i & -i)) ans += dp[i][j];
}else{
// 将 节 点 k 加 入 状 态
dp[i | 1 << k][k] += dp[i][j];
}
}
}
}
// 去除重复状态:①同一条路径出现两次【无 向 边】;②一条边和两个端点构成非法环。
cout << (ans - m) / 2 << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
t = 1;
for (int _ = 0; _ < t; _++)
solve();
return 0;
}
CF16E
题目链接
https://codeforces.com/problemset/problem/16/E
题目大意

代码
#include<bits/stdc++.h>
using namespace std;
const int N = 20;
int n, m, t;
double dp[1 << N],prob[N][N];
void solve() {
cin >> n;
for(int i = 0;i < n;++i){
for(int j = 0;j < n;++j){
cin >> prob[i][j];
}
}
// 初始化,所有鱼都在的概率为 1
dp[(1 << n) - 1] = 1;
for(int i = (1 << n) - 1;i > 0;--i){
double cnt = 0;
// 有 多 少 只 活 着 的 鱼 ?
for(int j = 0;j < n;++j){
if(i & (1 << j)) ++cnt;
}
// 挑 选 状 态 i 里 活 着 的 鱼 j 与 k!
for(int j = 0;j < n;++j){
if(!(i & (1 << j))) continue;
for(int k = j + 1;k < n;++k){
if(!(i & (1 << k))) continue;
// j 吃掉 k 活着的鱼状态为i的概率 * j吃掉k的概率 * (j 与 k 遇见的概率)
dp[i - (1 << k)] += dp[i] * prob[j][k] * (1.0 / (cnt * (cnt - 1) / 2));
// k 吃掉 j
dp[i - (1 << j)] += dp[i] * prob[k][j] * (1.0 / (cnt * (cnt - 1) / 2));
}
}
}
cout << setiosflags(ios::fixed);
for(int i = 0;i < n;++i){
cout << setprecision(6) << dp[1 << i] << ' ';
}
cout << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
t = 1;
for (int _ = 0; _ < t; _++)
solve();
return 0;
}

浙公网安备 33010602011771号