UVA_11008
这个题目由于树的数目很少,我们可以把树的状态压缩成一个整数,并依此来进行状态转移。
为了方面后续的状态转移,我们可以预处理出任意两个树连线上树的分布情况,处理的时候同样选择把状态压缩成一个整数,这样在后面就只需要一个位运算的式子就可以消去这条线上所有的树了。
这样对于一个树林的状态tree,我们就可以枚举其中两棵树,打掉这条线上的所有树就可以了,用记忆化搜索实现起来会比较方便。
此外,最后有可能会出现剩余一棵树的情况,对于这种情况我们没有必要单独列一个每次只打某一棵树的决策,因为如果剩余的不只一棵树,显然这样不是最优的,这样做反而会可能增加很多无用的状态,因此我们用一个if对一棵树的情况单独处理一下即可。同样的,如果当前所剩的树已经达到了要求,就可以直接返回0了。
#include<stdio.h>
#include<string.h>
#define MAXD 70000
#define MAXN 20
#define INF 1000000000
int N, M, limit, f[MAXD], x[MAXN], y[MAXN], g[MAXN][MAXN];
int dp(int tree)
{
int i, j, k, t, st, min = INF;
if(f[tree] != -1)
return f[tree];
for(i = 0, t = 0; i < N; i ++)
if((1 << i) & tree)
t ++;
if(t <= limit)
return f[tree] = 0;
if(t == 1)
return f[tree] = 1;
for(i = 0; i < N; i ++)
if((1 << i) & tree)
for(j = i + 1; j < N; j ++)
if((1 << j) & tree)
{
t = dp(tree & (~g[i][j]));
if(t + 1 < min)
min = t + 1;
}
return f[tree] = min;
}
void solve()
{
int i, j, k, res;
scanf("%d%d", &N, &M);
for(i = 0; i < N; i ++)
scanf("%d%d", &x[i], &y[i]);
memset(g, 0, sizeof(g));
for(i = 0; i < N; i ++)
for(j = 0; j < N; j ++)
if(i != j)
for(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] ++;
}
memset(f, -1, sizeof(f));
limit = N - M;
res = dp((1 << N) - 1);
printf("%d\n", res);
}
int main()
{
int t, tt;
scanf("%d", &t);
for(tt = 0; tt < t; tt ++)
{
if(tt)
printf("\n");
printf("Case #%d:\n", tt + 1);
solve();
}
return 0;
}