Lightoj 1018

题目链接:

  Lightoj 1018 - Brush (IV) 

题目描述:

  有n个点,问最少用几条无线长的线段把这n个点全部覆盖起来?
解题思路:
  n最大是16,就把所有的线段预处理出来用二进制保存下来,对每个状态枚举每条线段,然后就TLE,TEL,TEL。
  其实应该换一种枚举方式,先预处理出来每条线段,对每一个状态选择两个不在状态的点,然后画以两个点为端点的线,来进行状态转移。这个题目最重要的优化在于如何挑选不在状态的两个点,测试实力比较多,我们可以预处理出来每个状态未覆盖的点。

 1 #include <cmath>
 2 #include <queue>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <string>
 6 #include <iostream>
 7 #include <algorithm>
 8 using namespace std;
 9 
10 typedef long long LL;
11 const int maxn = 16;
12 const int INF = 0x3f3f3f3f;
13 struct node
14 {
15     int x, y;
16 }p[maxn];
17 
18 vector <int> G[70000];
19 int dp[1<<16], line[maxn][maxn];
20 
21 bool judge (int x, int y, int z)
22 {
23     return (p[x].x - p[y].x)*(p[x].y - p[z].y) == (p[x].y - p[y].y)*(p[x].x - p[z].x);
24 }
25 
26 int main ()
27 {
28     int T;
29 
30     for (int i=0; i<70000; i++)
31         for (int j=0; j<maxn; j++)
32             if ((i&(1<<j)) == 0)
33                 G[i].push_back(j);
34 
35     scanf ("%d", &T);
36 
37     for (int t=1; t<=T; t++)
38     {
39         int n;
40         scanf ("%d", &n);
41         memset (dp, INF, sizeof(dp));
42         memset (line, 0, sizeof(line));
43 
44         for (int i=0; i<n; i++)
45         {
46             scanf ("%d %d", &p[i].x, &p[i].y);
47             line[i][i] = (1<<i);
48         }
49         for (int i=0; i<n; i++)
50             for (int j=i+1; j<n; j++)
51             for (int k=0; k<n; k++)
52         {
53             if (!judge (i, j, k))   continue;
54             line[i][j] |= 1<<k;
55         }
56 
57         int cnt = (1<<n) - 1;
58         dp[0] = 0;
59         for (int i=0; i<cnt; i++)
60         {
61             int x = G[i][0];
62             for (int j=0; j<G[i].size(); j++)
63             {
64                 int y = G[i][j];
65                 dp[i|line[x][y]] = min (dp[i|line[x][y]], dp[i]+1);
66             }
67         }
68         printf ("Case %d: %d
", t, dp[cnt]);
69     }
70     return 0;
71 }
原文地址:https://www.cnblogs.com/alihenaixiao/p/4959439.html