题意:
给出n棵树的位置以及要消灭的树的数量m,同一直线上的树可一枪消灭,求最少枪支数
思路:
记忆化搜索,将树是否存在用二进制表示,一开始则是(1<<n-1)即都存在
然后枚举所有的树,与当前的状态st相与判断树是否消灭,然后枚举其后的数,再枚举可能与之前枚举的两棵在同一直线上的树,如果在直线上,则可消灭
最后取当前花费枪支的最小值
技巧
携带还需消灭的树会简化代码
代码:
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<iostream>
#define INF 0x3f3f3f3f
using namespace std;
const int N = (1<<16) + 100;
int dp[N], x[20], y[20];
int n, m;
int map[2005][2005];
bool ok(int i, int j, int k) {
int a = x[i]-x[j], b = y[i]-y[j];
int c = x[j]-x[k], d = y[j] - y[k];
return a*d == b*c;
}
int dp2(int k, int st) {
if(dp[st] != INF) return dp[st];
if(k==0) return 0;
if(k==1) return dp[st]=1;
for(int i=0; i<n; i++)
if((1<<i) & st) {
for(int j=i+1; j<n; j++) {
if((1<<j) & st ) {
int t = st, count=0;
for(int q=i; q<n; q++) {
if((1<<q) & st && ok(i, j, q)) {
t -= (1<<q);//用临时变量
count++;
}
}
dp[st] = min(dp[st], dp2(k-count, t)+1);
}
}
}
return dp[st];
}
int main() {
int kase;
int a, b;
int cas = 1;
scanf("%d", &kase);
while(kase--) {
scanf("%d%d", &n, &m);
for(int i=0; i<n; i++) {
scanf("%d%d", &x[i], &y[i]);
}
memset(dp, INF, sizeof(dp));
printf("Case #%d:\n",cas++);
printf("%d\n", dp2(m, (1<<n)-1));
if(kase)
puts("");
}
return 0;
}