CF1741G(状态压缩,DP)
CF1741G(状态压缩,DP)
题意
给一个无向连通图,有 \(f\) 个朋友在节点1,每个人的家在 \(h_i\) ,其中有 \(k(k \le 6)\)个朋友没有车,有车的朋友可以开车载任意数量的没车且家在有车朋友的回家的最短路上的没车朋友回家。问最后无法被开车送回的朋友的最小数量。
思路
\(k\) 的数量很小,可以考虑将朋友送到每个节点的状态压入 \(k\) 个bit位中。然后问题就变成对有车朋友最大化可行方案的bit位中1的个数。这个做一个可行性dp。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<bitset>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<cassert>
#include<functional>
#include<iomanip>
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3fll
#define endl '\n'
#define ll long long
// #define int long long
#define SZ(x) (int)x.size()
#define rep(i,a,n) for(int i = (a);i <= (n);i++)
#define dec(i,n,a) for(int i = (n);i >= (a);i--)
using namespace std;
using PII = pair<int, int>;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
const int N =10 + 2e5 ,mod=1e9 + 7;
/*
联通无向图, 起点1,朋友都在1,第i个朋友要去h_i
k <= 6 no car 。有车的朋友可以让他们搭便车,但只能沿着最短路走
输出最小走路的朋友
最多n方的可能状态,对每一个有车f考虑,使得或值最大
f[i][mask] |= f[i - 1][j] if(can find t | j == mask)
*/
void solve()
{
int n, m; cin >> n >> m;
vector<vector<int>> adj(n + 1);
rep(i, 1, m) {
int u, v; cin >> u >> v;
adj[u].emplace_back(v);
adj[v].emplace_back(u);
}
int fn; cin >> fn;
vector<int> h(fn + 1);
rep(i, 1, fn) cin >> h[i];
int k; cin >> k;
vector<int> p(k + 1);
rep(i, 1, k) cin >> p[i];
vector<int> state[n + 1];
vector<vector<bool>> ex(n + 1, vector<bool>(1 << k));
{
queue<PII> q; q.push({1, 0});
vector<bool> st(n + 1);
vector<int> dis(n + 1, -1); dis[1] = 0;
state[1].push_back(0);
ex[1][0] = 1;
auto chk = [&](int x) {
int ans = 0;
rep(i, 1, k) if(x == h[p[i]]) ans |= (1 << (i - 1));
return ans;
};
while(!q.empty()) {
auto [u, fa] = q.front(); q.pop();
if(~fa and dis[fa] + 1 == dis[u]) {
int where = chk(u);
for(auto x : state[fa]) if(!ex[u][where | x])
state[u].push_back(where | x), ex[u][where | x] = 1;
}
if(st[u]) continue;
st[u] = 1;
for(int v : adj[u]) if(v != fa) {
if(dis[v] == -1) dis[v] = dis[u] + 1;
q.push({v, u});
}
}
}
int ans = 0;
{
// f[i][mask] |= f[i - 1][j] if(can find t | j == mask)
vector<vector<int>> f(fn - k + 1, vector<int>(1 << k, 0));
f[0][0] = 1;
int cur = 1;
rep(i, 1, fn) {
bool jump = 0;
rep(j, 1, k) if(p[j] == i) jump = 1;
if(jump) continue;
rep(mask, 0, (1 << k) - 1) {
for(auto t : state[h[i]]) {
rep(j, 0, (1 << k) - 1) if(mask == (t | j)) {
f[cur][mask] |= f[cur - 1][j];
}
}
}
cur ++;
}
rep(mask, 0, (1 << k) - 1) if(f[fn - k][mask])
ans = max(ans, __builtin_popcount(mask));
}
cout << k - ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}