Codeforces Round #826 (Div. 3)G. Kirill and Company(状态压缩 + dp)

153 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
本文已参与「新人创作礼」活动,一 起开启掘金创作之路。

题目

image.png 中文大意

基里尔住在一个有n个顶点和m条边的连接无向图上,顶点1。一个美好的夜晚,他聚集了f个朋友,第i个朋友住在顶点hi。因此,所有的朋友现在都在顶点1,第i个朋友必须去他的家,去顶点hi。 晚会即将结束,是时候离开了。结果发现,他的朋友中有k(k≤6)人没有车,如果没有人送他们,他们就得步行。一个有车的朋友可以让任何数量的没有车的朋友搭车,但前提是他可以沿着一条最短的路径开到他家,让他们搭车。

解法

因为数据很小所以我们可以使用状压的写法(就是用二进制上的0和1来表示某种状态)我们用dp[i][j]dp[i][j]来表示我们从顶点1回到ii的最路路径上的状态为jj所以我们采用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;
   }

这样我们就可以得到了对于每一个点顶点到该点的最短路上的所有状态 然后我们对于所有的状态进行|的操作这样对所有方案书进行一个归类最终我们只需要遍历一下0630-63中状态成立的取最大值最后输出kmaxk - max即可

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;
}