Hlg 1067 【状态压缩DP】.cpp

题意:

  给出每一秒降落的蚊子坐标~

  给你一个蚊拍..

  如果一下子拍死n只那就能得n*n的分~

  问最高能得多少分~

 

  输入:

    一个t n 表示t秒n只蚊子

    接下来t行每行给出n只蚊子的横纵坐标

 

思路:

  因为n<10 所以可以用状态DP来找~

   dp[t][stat] 表示在第t次的stat状态下 eg:dp[2][4]表示在第二次4--> (0100)-->第3个位置有蚊子的状态下能够得到的分

    先把所有情况都初始化为负无穷大..

    然后把dp[0][0~stat]都初始化0

  

  因为落拍点的不同会导致拍到蚊子数的不同~

    即会存在偏移量~

    所以球拍落拍点应该先处理一下~

   偏移量的存在意义就是保证在起码拍到一只蚊子的前提下尽量枚举球拍落在的不同位置

 

  然后算出拍某一只蚊子会同时拍到多少只 存在num[i]内

  把落拍位置保存在pos[i]内

 

  再枚举每一个落拍点得到第i次的stat状态下可以拍到的蚊子数..

 

  动态转移方程为:dp[i][stat|j] = max(dp[i][stat|j], dp[i-1][j]+num[stat]*num[stat])

    

Tips:

  ①. 

    状态DP 用二进制的方法来表示了有和无..

    例如 0010 表示第二个位置有一只蚊子   01001 表示第一个位置和第四个位置有一只蚊子(即从右往左数..因为表示位置的时候也是第x个位置: 1<<x)

    & 可以得到该位置~ | 可以得到包含该位置的状态

   

   讲解过程:     

位运算符部分讲解
一开始的时候蚊子全都木有挂

假如是5个蚊子

用状态00000表示

假如达到了某个状态例如是

11010

这个状态就是表示第2,4,5只蚊子已经阵亡了

pos[k]表示蚊拍在k这个位置可以打到蚊子的情况

假如pos[k]=00111

这个就表示这个位置能打到第1,2,3只蚊子

stat=pos[k]^(pos[k]&j);

假如j是现在的状态11010

pos[k]&j的结果就是

 11010

&00111

=00010

这个表示现在的状态和蚊拍包括的同样蚊子的情况,例如这个就是表示之前的状态j和pos【k】里面都包含了覆盖第2只蚊子的情况

把这个状态和pos[k]异或

 00010

^00111

=00101

这个就表示蚊拍打的位置中能打到之前没打到的蚊子的情况,例如这个就表示能打到第1,3只蚊子,并且这两只在j状态里面没有包含

 

Code:

View Code
 1 #include <stdio.h>

 2 #include <cstring>

 3 #include <cmath>

 4 #include <algorithm>

 5 using namespace std;

 6 #define eps 1e-8      ///1e-2??

 7 #define clr(x) memset(x, 0, sizeof(x))

 8 #define INF 0x1f1f1f1f

 9 const int MAXN = 12;

10 

11 int n, t;

12 double r;

13 double x[MAXN], y[MAXN];

14 double xp[MAXN*6], yp[MAXN*6];

15 int totx, toty;

16 bool vis[1<<MAXN];

17 int pos[1<<MAXN], dp[MAXN][1<<MAXN], num[1<<MAXN];

18 int tot, st, ans;

19 

20 void ini()

21 {

22     st = 1<<n;

23     clr(pos), clr(num);

24   //  memset(dp, -INF, sizeof(dp));

25 

26     for(int i = 0; i <= t; ++i)

27         for(int j = 0; j < st; ++j)

28             dp[i][j] = -INF;

29 

30     for(int i = 0; i <= st; ++i)

31         dp[0][i] = 0;

32     for(int i = 0; i < st; ++i)

33         for(int j = 0; j < n; ++j)

34             if(i&(1<<j)) num[i]++;

35 }

36 

37 void DP()

38 {

39     totx = toty = 0;

40     tot = 0;

41     clr(vis);

42 

43     for(int i = 0; i < n; ++i) {

44         xp[totx++] = x[i]-eps, xp[totx++] = x[i], xp[totx++] = x[i]+eps;

45         xp[totx++] = x[i]-r-eps, xp[totx++] = x[i]-r, xp[totx++] = x[i]-r+eps;

46         yp[toty++] = y[i]-eps, yp[toty++] = y[i], xp[toty++] = y[i]+eps;

47         yp[toty++] = y[i]-r-eps, yp[toty++] = y[i]-r, yp[toty++] = y[i]-r+eps;

48     }

49     sort(xp, xp+totx), sort(yp, yp+toty);

50     for(int i = 0; i < totx; ++i)

51         for(int j = 0; j < toty; ++j) {

52             int stat = 0;

53             for(int k = 0; k < n; ++k) {

54                 if((xp[i] < x[k]+eps && x[k]-eps < xp[i]+r) &&

55                    (yp[j] < y[k]+eps && y[k]-eps < yp[j]+r))

56                     stat |= (1<<k);

57             }

58             if(stat > 0 && !vis[stat]) {

59                 pos[tot++] = stat;

60                 vis[stat] = true;

61             }

62         }

63 }

64 

65 int main()

66 {

67     while(EOF != scanf("%d %d", &n, &t)) {

68         scanf("%lf", &r);

69         ini();

70 

71         for(int i = 1; i <= t; ++i) {

72             for(int j = 0; j < n; ++j)

73                 scanf("%lf %lf", &x[j], &y[j]);

74             DP();

75             for(int j = 0; j < st; ++j)

76                 for(int k = 0; k < tot; ++k) {

77                     int stat = pos[k]^(pos[k]&j);

78                     if(stat > 0)

79                         dp[i][j|stat] = max(dp[i][j|stat], dp[i-1][j]+num[stat]*num[stat]);

80                 }

81         }

82         ans = 0;

83         for(int i = 0; i < st; ++i)

84             ans = max(ans, dp[t][i]);

85         printf("%d\n", ans);

86     }

87     return 0;

88 }

 

链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1067

你可能感兴趣的:(cpp)