Problem E
Antimatter Ray Clearcutting
Input: Standard Input
Output: Standard Output
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 integers n (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).
题意:N组样例,给定n课树的坐标,现在最少要砍掉m颗树,你可以发射光线,光线可以砍掉一整条直线上的树,问最少要几条光线能完成任务。
思路:记忆化搜索。先把所有两点间确定的直线保存下来。保存方式为用一个2进制数,0表示没,1表示有树,保存下这条直线能砍下得树。然后进行记忆化搜索。dp[ss]。ss也是一个二进制数,一开始为111111111,表示所有树都在。最后要注意剩下一棵树的时候是需要多一条光线的。
代码:
#include <stdio.h> #include <string.h> #define INF 0x3f3f3f3f const int MAXN = 25; int min(int a, int b) {return a < b ? a : b;} int N; int n, m, i, j, k, line[MAXN][MAXN], dp[1<<20]; struct Point { int x, y; } p[MAXN]; int DP(int ss) { if (dp[ss] != -1) return dp[ss]; int &ans = dp[ss]; int count = 0; for (int ii = 0; ii < n; ii ++) { if ((1 << ii) & ss) count ++; } if (count <= (n - m)) return ans = 0; if (count == 1) return ans = 1; ans = INF; for (int i = 0; i < n; i ++) { if (ss & (1 << i)) for (int j = i + 1; j < n; j ++) { if (ss & (1 << j)) { ans = min(ans, DP(ss&(~line[i][j])) + 1); } } } return ans; } int main() { int T = 0; scanf("%d", &N); while (N --) { memset(line, 0, sizeof(line)); memset(dp, -1, sizeof(dp)); scanf("%d%d", &n, &m); for (i = 0; i < n; i ++) { scanf("%d%d", &p[i].x, &p[i].y); } for (i = 0; i < n; i ++) { for (j = i + 1; j < n; j ++) { int s = 0; for (k = n - 1; k >= 0; k --) { if ((p[i].x - p[k].x) * (p[j].y - p[k].y) == (p[j].x - p[k].x) * (p[i].y - p[k].y)) { s = s * 2 + 1; } else { s *= 2; } } line[i][j] = line[j][i] = s; } } printf("Case #%d:\n", ++ T); printf("%d\n", DP((1<<n) - 1)); if (N) printf("\n"); } return 0; }