首页 > 其他分享 >HDU-4281 Judges' response(2012 ACM/ICPC Asia Regional Tianjin Online)

HDU-4281 Judges' response(2012 ACM/ICPC Asia Regional Tianjin Online)

时间:2024-06-22 19:20:41浏览次数:14  
标签:HDU 4281 return 裁判 Tianjin res ++ int dp

HDU-4281 Judges' response(2012 ACM/ICPC Asia Regional Tianjin Online)

状态压缩 + 01背包 + 区间dp

题意:

有n个地点,第一个地点是裁判位置,其他n-1个地点是选手的位置。裁判要给选手解决问题,每个选手都有一个时间表示解决这个选手问题所需要的时间。同样的,裁判也有一个时间,表示这个裁判的耐心值。

问题有两个:

  • 问最少需要多少裁判能解决选手所有的问题
  • 问在可以用无限裁判的情况下(依然考虑裁判的耐心值),裁判到达所有选手位置,再回到裁判原来的位置最少的时间

思路:

对于第一个问题:

类似于[小猫爬山][https://www.acwing.com/problem/content/description/167/],感觉很典,用dfs剪枝好像更快一点。

dfs剪枝思路:

数组倒序排,然后dfs剪枝能填就填。但是在这个题里要特判一下如果有一个人的解答时间超过裁判的耐心值就输出-1

剪枝的复杂度证明不了,但是比状压快

   auto f =[&] () {
      vector<int> v;
      for (int i = 1; i < n; i++) {
         v.push_back(c[i]);
      }

      sort(v.begin(), v.end(), greater<int>());
      if(v[0] > m) return inf;
      vector<int> vis(n + 10, 0);
      int res = inf;

      function<void(int, int)> dfs =[&] (int now, int cnt) {
         if(cnt >= res) {
            return ;
         }
         if(now == n - 1) {
            res = min(res, cnt);
            return ;
         }
         for (int i = 1; i <= cnt; i++) {
            if(vis[i] + v[now] <= m) {
               vis[i] += v[now];
               dfs(now + 1, cnt);
               vis[i] -= v[now];
            }
         }

         vis[cnt + 1] = v[now];
         dfs(now + 1, cnt + 1);
         vis[cnt + 1] = 0;
      };

      dfs(0, 1);
      return res;

   };
状态压缩 + 01背包:

我感觉很妙,先把一个教练能解答的情况表示出来。

情况怎么表示?二进制枚举,0表示不解答,1表示解答。

把一个人能解决的情况,也就是这几个人的解答时间之和不会超过耐心值。而且要注意,这个情况是一个人能完成的所有情况而不是最大情况。

这些压缩后的二进制看成01背包的物品价值,重量都是1。

   for (int i = 0; i < (1 << n); i++) {
      if(check(i)) {
         statue.push_back(i);
      }
   }

   auto dp01 =[&] () {
      vector<int> dp((1 << n) + 10, inf);
      dp[0] = 0;

      for (int step : statue) {
         for (int j = (1 << n) - 1; j >= 0; j--) {
            if(dp[j] != inf && !(j & step)) {
               dp[j + step] = min(dp[j + step], dp[j] + 1);
            }
         }
      }

      return dp[(1 << n) - 1];
   };

最后特判一下,-1的情况

   int ans1 = f();
   // int ans1 = dp01();
   if(ans1 == inf) {
      cout << -1 << " " << -1 << "\n";
      return;
   }
   cout << ans1 << ' ';

对于第二个问题:

我们怎么在M(裁判耐心值)的限制下完成这个旅行商问题?

原来的旅行商是一个人可以走很多地方。但是这个问题里有了限制,裁判就不一定能走到所有地方了。

那我们应该把这个情况先表示出来。每一个都是独立的。每一次走完都要回到原点,把这几次的情况用区间dp合并就好了。

我们现在用三个数组表示状态。

$ tem[i][j] 表示走完j状态,停到i点$

$ best[i] 表示走完j状态,停到原点的最短时间$

两个转移方程:

  • $tem[k][s | (1 << k)] = min(tem[k][s | (1 << k)], tem[j][s] + dis[j][k]);$

  • $best[s] = min(best[s], tem[j][s] + dis[j][0]);$

这两个dp完成之后我们得到了best[i]。也就是一个人能到达的情况的最优值。

最后只需要把这些情况合并就好。

   for (int i = 0; i < (1 << n); i++) {
      if(i & 1) {
         for (int j = i; j; j = i & (j - 1)) {
               best[i] = min(best[i], best[j | 1] + best[(i - j) | 1]);
         }
      }
   }

有两个需要注意的地方:

  • i&1,也就是这个情况必须从1点出发,不然不合法
  • 第二层循环的循环方式,就是枚举掩码的所有子掩码的方式,为的就是两个情况去的点不重复。

完整代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <functional>

using namespace std;

const int inf = 1e9;
void solve(int n, int m) {
   vector<int> x(n + 1), y(n + 1), c(n + 1);
   for (int i = 0; i < n; i++) cin >> x[i] >> y[i];
   for (int i = 0; i < n; i++) cin >> c[i];

   vector<int> statue;
   auto check =[&] (int t) {
      int res = 0;
      for (int i = 0; i < n; i++) {
         if((1 << i) & t) {
            res += c[i];
         }
      }
      return res <= m;
   };

   for (int i = 0; i < (1 << n); i++) {
      if(check(i)) {
         statue.push_back(i);
      }
   }

   auto dp01 =[&] () {
      vector<int> dp((1 << n) + 10, inf);
      dp[0] = 0;

      for (int step : statue) {
         for (int j = (1 << n) - 1; j >= 0; j--) {
            if(dp[j] != inf && !(j & step)) {
               dp[j + step] = min(dp[j + step], dp[j] + 1);
            }
         }
      }

      return dp[(1 << n) - 1];
   };

   auto f =[&] () {
      vector<int> v;
      for (int i = 1; i < n; i++) {
         v.push_back(c[i]);
      }

      sort(v.begin(), v.end(), greater<int>());
      if(v[0] > m) return inf;
      vector<int> vis(n + 10, 0);
      int res = inf;

      function<void(int, int)> dfs =[&] (int now, int cnt) {
         if(cnt >= res) {
            return ;
         }
         if(now == n - 1) {
            res = min(res, cnt);
            return ;
         }
         for (int i = 1; i <= cnt; i++) {
            if(vis[i] + v[now] <= m) {
               vis[i] += v[now];
               dfs(now + 1, cnt);
               vis[i] -= v[now];
            }
         }

         vis[cnt + 1] = v[now];
         dfs(now + 1, cnt + 1);
         vis[cnt + 1] = 0;
      };

      dfs(0, 1);
      return res;

   };

   int ans1 = f();
   // int ans1 = dp01();
   if(ans1 == inf) {
      cout << -1 << " " << -1 << "\n";
      return;
   }
   cout << ans1 << ' ';


   vector<int> best((1 << n) + 10, inf);
   vector<vector<int>> tem(n + 10, vector<int>((1 << n) + 10, inf));
   vector<vector<int>> dis(n + 10, vector<int>(n + 1));
   tem[0][1] = 0;

   auto cal =[&] (int a, int b) {
      return ceil(sqrt((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b])));
   };

   for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
         dis[i][j] = cal(i, j);
      }
   }

   for (auto s : statue) {
      for (int j = 0; j < n; j++) {
         for (int k = 0; k < n; k++) {
            if(!((1 << k) & s)) {
               tem[k][s | (1 << k)] = min(tem[k][s | (1 << k)], tem[j][s] + dis[j][k]);
            }
         }
      }
   }
   for (auto s : statue) {
      for (int j = 0; j < n; j++) {
         if((1 << j) & s) {
            best[s] = min(best[s], tem[j][s] + dis[j][0]);
         }
      }
   }


   for (int i = 0; i < (1 << n); i++) {
      if(i & 1) {
         for (int j = i; j; j = i & (j - 1)) {
               best[i] = min(best[i], best[j | 1] + best[(i - j) | 1]);
         }
      }
   }

   cout << best[(1 << n) - 1] << "\n";
}

signed main() {
   ios::sync_with_stdio(0);
   cin.tie(0), cout.tie(0);

   int n, m;
   while(cin >> n >> m) {
      // cin >> n >> m;
      solve(n, m);
   }

   return 0;
}

标签:HDU,4281,return,裁判,Tianjin,res,++,int,dp
From: https://www.cnblogs.com/yyc4591/p/18262637

相关文章

  • Railway HDU - 3394 求调
    做个记录,如果有人愿意帮我调蒟蒻将感激不尽qwq#include<iostream>#include<iomanip>#include<cstdio>#include<vector>#include<stack>#include<queue>#include<bitset>#include<map>#include<set>#include<unorde......
  • hdu2845dp问题
    看了一眼题目,简单dp问题,但超时了一晚上,试了各种方法无法解决,最终放弃java,改用C直接过,我哭了。。。。#include<stdio.h>#include<string.h>#definemaxn200010intdp[maxn],ans[maxn],map[maxn];intmax(intx,inty){returnx>y?x:y;}intmain(){inti,j;......
  • hdu1421搬寝室dp
    状态转移方程if(j==2*i+1){ dp[j][i]=dp[j-2][i-1]+(val[j]-val[j-1])*(val[j]-val[j-1]); }else{ dp[j][i]=Math.min(dp[j-1][i],dp[j-2][i-1]+(val[j]-val[j-1])*(val[j]-val[j-1])); } importjava.util.Arrays;importjava.util.S......
  • HDU 3642 (扫描线、三维体积相交)
    题意在三维空间中给你n个长方体,求空间中被这些长方体覆盖至少3次以上的区域的总体积。思路这题没给数据组数T的范围,大致看了一下其他人的都是枚举z来做的,所以我这边也是同样的做法转换成二维的扫描线来做,数组ci表示被覆盖i次的区间标记,具体扫描线怎么实现可以看我上篇博客。......
  • hdu5532
    给定一个序列,询问是否能删除一个数让它成为非递减或者非递增的序列。nlogn一直过不了,所以选择了以下方式。。。因为删除一个嘛,先证明删除一个能不能是非递减的(非递增的把序列倒过来搞一次就行了)首先,对一个序列前后两个数做差比如说序列31415 做差后(即1-3,4-1,1-4,5-1)是......
  • hdu1087简单动态规划
    求最长子序列的和。dp[i]=max(dp[i],dp[j]+xx[i])。importjava.util.Arrays;importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){//TODO自动生成的方法存根Scannersc=newScanner(System.in);......
  • hdu1257最少拦截系统
    Dilworth定理通俗地讲就是对于一个偏序集,最少链划分等于最长反链长度。通俗点就是一个数列最少的不上升(<=)子序列的条数等于该数列最长上升(>)子序列的长度就是求最长有序子序列packagebag;importjava.util.Arrays;importjava.util.Scanner;publicclasshdu1257{......
  • hdu1069java
    给你n个方块,其中每个方块具有它的长宽高(方块可以任意旋转放置),方块数量不限。现在你要堆一个高塔,上面方块的长和宽必须严格小于下面方块的长和宽。问你能堆起来的最大高度。先将方块以长和宽按从小到大排序,然后从小到大以此为底,求出最大高度。dp[i]=max(dp[j])+i.height(j.x<i.x......
  • hduoj 1506(笛卡尔树)
    Problem-1506(hdu.edu.cn)题意坐标轴给定一些矩形,紧密排在一起,每个矩形宽度固定为1,问形成的图案中最大可以组成的矩形面积。思路常规思路是可以用单调栈分别找两边的合法边界,这里使用笛卡尔树。笛卡尔树实现了堆的性质,并且对原数组建立的笛卡尔树进行中序遍历为原数组的顺......
  • hdu4417(权值离散化后建立主席树)
    Problem-4417(hdu.edu.cn)马里奥是举世闻名的水管工。他“魁梧”的身材和惊人的跳跃能力让我们想起了。现在可怜的公主又遇到了麻烦,马里奥需要拯救他的爱人。我们将通往老板城堡的道路视为一条线(长度为n),在每个整数点i上都有一块高度为hi的砖。现在的问题是,如果马里奥可......