持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
本文已参与「新人创作礼」活动,一 起开启掘金创作之路。
题目
中文大意
基里尔住在一个有n个顶点和m条边的连接无向图上,顶点1。一个美好的夜晚,他聚集了f个朋友,第i个朋友住在顶点hi。因此,所有的朋友现在都在顶点1,第i个朋友必须去他的家,去顶点hi。 晚会即将结束,是时候离开了。结果发现,他的朋友中有k(k≤6)人没有车,如果没有人送他们,他们就得步行。一个有车的朋友可以让任何数量的没有车的朋友搭车,但前提是他可以沿着一条最短的路径开到他家,让他们搭车。
解法
因为数据很小所以我们可以使用状压的写法(就是用二进制上的0和1来表示某种状态)我们用来表示我们从顶点1回到的最路路径上的状态为所以我们采用BFS来一层一层去找最短路
dp[1][0] = 1;
vis[1] = true;
while(q.size()) {
vector<PII> ne;
for(auto [x,y] : q) vis[x] = true;//表示这个点到达过了
for(auto [x,y] : q) {
for(auto u : v[x]) {
int st = y;//继承上一个点的状态并且进行修改
rep(i,k) if(u == h[p[i]]) st |= 1 << (i - 1);
if(!vis[u] &&!dp[u][st]) {
ne.pb({u,st});
dp[u][st] = 1;
}
}
}
q = ne;
}
这样我们就可以得到了对于每一个点顶点到该点的最短路上的所有状态 然后我们对于所有的状态进行|的操作这样对所有方案书进行一个归类最终我们只需要遍历一下中状态成立的取最大值最后输出即可
int run(int x) {
int cnt = 0;
while (x) cnt += x & 1, x /= 2;
return cnt;
}
vector<int> v[N];
int h[N],p[N];
bool dp[N][1 << 7];
bool vis[N];
void solve()
{
int n,m; cin >> n >> m;
rep(i,n) v[i].clear(),memset(dp[i],0,sizeof(dp[i]));
rep(i,n) vis[i] = 0;
rep(i,m) {
int x,y; cin >> x >> y;
v[x].pb(y);
v[y].pb(x);
}
int f; cin >> f;
rep(i,f) cin >> h[i];
int k; cin >> k;
rep(i,k) cin >> p[i];
vector<PII> q;
q.pb({1,0});
dp[1][0] = 1;
vis[1] = true;
while(q.size()) {
vector<PII> ne;
for(auto [x,y] : q) vis[x] = true;
for(auto [x,y] : q) {
for(auto u : v[x]) {
int st = y;
rep(i,k) if(u == h[p[i]]) st |= 1 << (i - 1);
if(!vis[u] &&!dp[u][st]) {
ne.pb({u,st});
dp[u][st] = 1;
}
}
}
q = ne;
}
rep(i,k) h[p[i]] = 0;
bitset<64>pre, now;
pre[0] = 1;
for (int i = 1; i <= f; i++) {
if (h[i] == 0)continue;
int u = h[i];
now = pre;
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
if (dp[u][y]) {
now[y | x] = now[y | x] || pre[x];
}
}
}
pre = now;
}
int res = 0;
for(int i = 0; i < 64; i++) {
if(now[i]) res = max(res,run(i));
}
cout << k - res << endl;
}