# 位元dp
* 用0, 1代表一個物件選或不選的狀態,壓縮成二進位集合,所以可以同時代表多個物件的01的狀態
位元操作
---
* 在k位元新增1:```n | (1<<k)```
* 在k位元刪除1:```n ^ (1<<k)```
c460: 3. 軍隊部署
---
https://zerojudge.tw/ShowProblem?problemid=c460
* 不停的or,也就是一直計算各角色總共覆蓋到的技能,把每種覆蓋的組合壓縮成二進位存在dp
```cpp=
#include <bits/stdc++.h>
#define EB emplace_back
#define vci vector<int>
#define PII pair<int,int>
#define F first
#define S second
#define rep(X, a,b) for(int X=a;X<b;++X)
#define ALL(a) (a).begin(), (a).end()
#define SZ(a) (int)(a).size()
#define NL "\n"
#define LL long long
using namespace std;
LL dp[10]={0}, ft[10]={0}, sec[10]={0}, thd[10]={0}, dp2[10]={0};
int dec(int a, int b, int c){
int res=0;
if(a) res=1;
if(b) res|=1<<1;
if(c) res|=1<<2;
return res;
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, a, x;
cin>>n;
rep(i,0,n){
cin>>a;
int q,w,e;
cin>>q>>w>>e;
if(a==1) ft[dec(q,w,e)]+=1;
else if(a==2) sec[dec(q,w,e)]+=1;
else thd[dec(q,w,e)]+=1;
}
// 2
rep(i,0,8){
rep(j,0,8){
dp[i|j]+=sec[i]*ft[j];
}
}
// 3;
rep(i,0,8){
rep(j,0,8){
dp2[i|j]+=thd[i]*dp[j];
}
}
cout<<dp2[7]<<NL;
}
```
traveling salesman problem
---
https://judge.tcirc.tw/ShowProblem?problemid=d089
* 設家為m
* ```dp[i][a]```: 從i開始到m經過所有**二進位a中為1的位置**一次的最短路徑
* 初始化所有點到m的距離(包括m到其他一個點再回到m的距離)
* 生成所有二進位組合a(從3個開始),並算出所有起點經過a集合中的最短路徑(枚舉下一個點做轉移)
* 轉移:```dp[i][a] = min(dp[i][a], dis[i][j]+dp[j][a & ~(1<<i)])```
* i為起點,只能是a中為1的點
* a為集合
* j是i下一個的點,枚舉j,要為a中為1的點
* ```dis[i][j]+dp[j][a & ~(1<<i)]```為i到j距離加上從j開始但不經過i的最短路徑(用bitmask)
* 要特別計算i(起點)=m的值,在同個集合a計算完所有i!=m後,計算從i=m開始經過集合所有點(m為終點一樣要經過)的值
* 轉移:```dp[i][a] = min(dp[i][a], dis[i][j]+dp[j][a])```
```cpp=
#include <bits/stdc++.h>
#define EB emplace_back
#define vci vector<int>
#define PII pair<int,int>
#define F first
#define S second
#define rep(X, a,b) for(int X=a;X<b;++X)
#define ALL(a) (a).begin(), (a).end()
#define SZ(a) (int)(a).size()
#define NL "\n"
#define LL long long
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
using namespace std;
LL dp[20][300000]={0};
int dis[20][20]; //i: start point, j:subset
vector<int> subset;
int n,m;
void combination(int k, int set, int at){
rep(i,at,n){
set|=1<<i;
if(k==1) subset.EB(set);
else combination(k-1, set, i+1);
set&=~(1<<i); // unset 1 in pos i
}
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
rep(i,0,n)
rep(j,0,n)
cin>>dis[i][j];
// initialize every start point
for(int i=0;i<n;++i){
if(i==m) continue;
dp[i][1<<m | 1<<i]=dis[i][m];
}
// starting from home
for(int i=0;i<n;++i){
if(i==m) continue;
dp[m][1<<m | 1<<i]=dis[i][m]*2;
}
// bitset with k bits of 1
for(int k=3;k<=n;++k){
subset.clear();
combination(k, 0, 0);
for(auto a:subset){
if(!(a & 1<<m)) continue;// if bitset doesn't contain home exclude it
for(int i=0;i<n;++i){
if(i==m || !(a & 1<<i)) continue;
dp[i][a]=INT_MAX;
for(int next=0;next<n;++next){
if(i==next || !(a & 1<<next)) continue;
dp[i][a]=min(dp[i][a], dis[i][next]+dp[next][a & ~(1<<i)]);
}
}
// from home
dp[m][a]=INT_MAX;
for(int next=0;next<n;++next){
if(m==next || !(a & 1<<next)) continue;
dp[m][a]=min(dp[m][a], dis[m][next]+dp[next][a]);
}
}
}
int all=0;
for(int i=0;i<n;++i) all|=1<<i;
cout<<dp[m][all]<<NL;
}
```