uva 11008 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).
题目大意:有n棵树,要砍掉m棵,你有一把可以射一条直线的枪,可以帮助你砍掉一条线上所有的树,但是这种枪的子弹非常贵,所以需要你来算出最少需要多少枪,就可以砍掉所有的树。
解题思路:将n棵树的状态压缩为0和1,1代表树还在,0代表数已经被砍了。DFS时,先找出还未被砍掉的两棵树,然后找出在这两棵树连成的直线上的未被砍掉的树(包括其本身两点),将该树的位数置为0,然后以新的状态进入下一层DFS,直到砍完m棵树,进行对Min的维护。
PS:当下一层DFS完结时,要记得恢复这一层砍掉的树的位数(回溯)。当只剩一棵树时,需要特判。要记录每一个状态,防止重复搜查。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct poi{
int x, y;
} p[25];
int rec[25];
int vis[1 << 20];
int n, m, Min;
int check(poi a, poi b, poi c) {
int px = b.x - a.x, py = b.y - a.y;
int qx = c.x - b.x, qy = c.y - b.y;
if ((qx * py) == (qy * px)) return 1;
else return 0;
}
int DFS(ll now, int cur, int cnt) {
if (vis[now]) return 0; //dp数组记录的是当前压缩状态now下的最小
else if (cur <= 0) {
if (cnt < Min) Min = cnt;
return 0;
} else if(cur == 1) { //只剩一棵树
Min = min(Min, cnt + 1);
return 0;
}
if (cnt >= Min) return 0;
for (int i = 0; i < n; i++) {
if ( !(now & (1 << i)) ) continue;
for (int j = i + 1; j < n; j++) {
if ( !(now & (1 << j)) ) continue;
int c = 0;
int tmp = now;
for (int k = i; k < n; k++) {
if (now & (1 << k) && check(p[i], p[j], p[k])) {
c++;
tmp -= (1 << k);
}
}
vis[now] = 1;
DFS(tmp, cur - c, cnt + 1);
}
}
}
int main() {
int T, Case = 1;
scanf("%d", &T);
while (T--) {
memset(vis, 0, sizeof(vis));
printf("Case #%d:\n", Case++);
Min = 20;
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d %d", &p[i].x, &p[i].y);
}
ll now = (1 << n) - 1;
DFS(now, m, 0);
printf("%d\n", Min);
if (T) printf("\n");
}
return 0;
}