UVa 11008 Antimatter Ray Clearcutting(记忆化搜索)

题意:

n个坐标上面分别有n棵树,每发射一次枪,可以把一条直线上面的树都消灭掉。

问要消灭到m棵树,最少需要几枪。

思路:

记忆化搜索。原则上每枪最少要消灭2棵树,如果最后一枪下去导致剩下的树小于等于(n-m),则控制最后一枪使结果恰好等于结果。其实发射枪的次数还是不变的。

用一个整数的位表示剩下的树,预处理的过程中,g[i, j]表示与i树和j树在一条直线上面的树。

#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #include <algorithm>

using namespace std; const int MAXN = 20; const int MAXD = 65600; int f[MAXD]; int x[MAXN], y[MAXN]; int g[MAXN][MAXN]; int n, m, d; void init() { memset(g, 0, sizeof(g)); for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) if (i != j) for (int k = n - 1; k >= 0; --k) { g[i][j] <<= 1; if((y[j] - y[i]) * (x[k] - x[i]) == (x[j] - x[i]) * (y[k] - y[i])) ++g[i][j]; } } int dp(int u) { if (f[u] != -1) return f[u]; int c = 0; for (int i = 0; i < n; ++i) if (u & (1<<i)) ++c; if (c <= d) return f[u] = 0; if (c == 1) return f[u] = 1; int ans = INT_MAX; for (int i = 0; i < n; ++i) if (u & (1<<i)) for (int j = i + 1; j < n; ++j) if (u & (1<<j)) ans = min(ans, dp(u & (~g[i][j])) + 1); return f[u] = ans; } int main() { int cases, count = 0; scanf("%d", &cases); while (cases--) { scanf("%d %d", &n, &m); for (int i = 0; i < n; ++i) scanf("%d %d", &x[i], &y[i]); init(); memset(f, -1, sizeof(f)); d = n - m; int ans = dp((1<<n) - 1); printf("Case #%d:\n", ++count); printf("%d\n", ans); if (cases) printf("\n"); } return 0; }

 

 

你可能感兴趣的:(clear)