Uva--11008(动规,状态压缩,记忆化搜索)
2014-08-25 00:52:40
Antimatter Ray Clearcutting
It's year 2465, and you are the Chief Engineer for Glorified Lumberjacks Inc. on planet Trie. There is a number of trees that you need to cut down, and the only weapon you have is a high-powered antimatter ray that will cut through trees like butter. Fuel cells for the antimatter ray are very expensive, so your strategy is: stand somewhere in the forest and shoot the ray in some chosen direction. This will cut down all the trees that lie on the line in that direction. Given the locations of several trees and the number of trees that you are required to cut, what is the minimum number of shots that you need to fire?
Input
The first line of input gives the number of cases, N (at most 20). N test cases follow. Each one starts with 2 lines containing the integersn (the number of trees in the forest, at most 16) and m (the number of trees you need to cut, at most n). The next n lines will each give the (x,y) coordinates of a tree (integers in the range [-1000, 1000]).
Output
For each test case, output the line "Case #x:", where x is the number of the test case. On the next line, print the number of antimatter ray shots required to cut down at least m trees. Print an empty line between test cases.
Sample Input Output for Sample Input
2 4 4 0 0 0 1 1 0 1 1 9 7 0 0 1 1 0 2 2 0 2 2 3 0 3 1 3 2 3 4 |
Case #1:
2
Case #2:
2
|
Notes
In the first test case, you can cut down 4 trees by standing at (0, -1) and firing north (cutting 2 trees) and then standing at (1, -1) and again firing north (cutting 2 more trees).
In the second test case, you should stand at (3,-1) and fire north (cutting 4 trees) and then stand at (-1, -1) and fire north-east (cutting 3 more trees).
思路:这题,看了下数据范围,树最多16棵,又看了看题意,果断把所有树看成集合压缩进一个二进制数(最大不超1 << 16)最大中。然后自己就开始愉快地瞎YY了。QAQ。
(1)首先把输入的树的坐标做一个预处理,用mp[i][j][p]来表示,第 p 棵树是否在 射线第 i 棵 -> 第 j 棵上。
(2)开始记忆化搜索,dp[i][j]表示对于当前状态 i (i表示数的集合压缩成二进制的数)再cut掉 j 棵树所需要的最小fire次数。
(3)当前状态 i ,枚举选出两个点 a, b,把在射线 a -> b 树全部cut掉,如果cut掉的树 <= 所需要的cut掉的树,就可以以新的状态 ti (从状态 i cut掉树以后的状态)开始新的搜索。
话外:关于这题是否需要考虑在树林当中fire,还是仅考虑在树林周围fire不是很清楚(其实就是考虑用射线还是直线的问题),但个人觉得用射线更严谨。这题写的效率不高,2162ms。
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cmath> 5 using namespace std; 6 const int RA = 1 << 16; 7 const int INF = 1 << 30; 8 9 int Case; 10 int n,m; 11 int x[20]; 12 int y[20]; 13 int dp[RA + 5][17]; 14 int mp[20][20][20]; 15 16 int Dfs(int v,int k){ 17 if(dp[v][k] != -1){ 18 return dp[v][k]; 19 } 20 int &res = dp[v][k]; 21 res = INF; 22 for(int i = 0; i < n; ++i){ 23 if((v & (1 << i)) == 0) 24 continue; 25 for(int j = i; j < n; ++j){ 26 if((v & (1 << j)) == 0) 27 continue; 28 int next_v = v,cnt = 0; 29 for(int p = 0; p < n; ++p){ 30 if(mp[i][j][p] && (next_v & (1 << p))){ 31 next_v ^= (1 << p); 32 cnt++; 33 } 34 } 35 if(k - cnt > 0){ 36 res = min(res,Dfs(next_v,k - cnt) + 1); 37 } 38 if(k - cnt == 0){ 39 return res = 1; 40 } 41 } 42 } 43 return res; 44 } 45 46 int main(){ 47 //freopen("in.txt","r",stdin); 48 scanf("%d",&Case); 49 for(int t = 1; t <= Case; ++t){ 50 memset(dp,-1,sizeof(dp)); 51 memset(mp,0,sizeof(mp)); 52 scanf("%d%d",&n,&m); 53 for(int i = 0; i < n; ++i){ 54 scanf("%d%d",&x[i],&y[i]); 55 } 56 for(int i = 0; i < n; ++i){ 57 mp[i][i][i] = 1; 58 for(int j = 0; j < n; ++j){ 59 if(i == j) 60 continue; 61 for(int p = 0; p < n; ++p){ 62 if(x[j] > x[i] && x[p] < x[i]) 63 continue; 64 if(y[j] > y[i] && y[p] < y[i]) 65 continue; 66 if((x[p] - x[i]) * (y[j] - y[i]) == (x[j] - x[i]) * (y[p] - y[i])){ 67 mp[i][j][p] = 1; 68 } 69 } 70 } 71 } 72 if(t != 1) 73 puts(""); 74 printf("Case #%d:\n",t); 75 printf("%d\n",m == 0 ? 0 : Dfs((1 << n) - 1,m)); 76 } 77 return 0; 78 }

浙公网安备 33010602011771号