Lightoj 1018 - Brush (IV) (状压DP)
题目链接:
题目描述:
有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\n", t, dp[cnt]); 69 } 70 return 0; 71 }
本文为博主原创文章,未经博主允许不得转载。