题意:给n个点,问最少需要几条直线覆盖所有点
这是一道状压dp,还要记忆化搜索,复杂度并不清楚,以后再解决
dp[s]:最少需要几条直线覆盖s中的点
转移时点数小于等于2时容易解决
如果点数大于2,先选定一点,再枚举另一个点
若选定点i,j,令ns为添加直线pi-pj后剩下的点
则还需要 dp[ns] 条直线来覆盖所有的点
预处理line[i][j]:直线pi-pj所能覆盖的点
#include <cstdio>
#include <algorithm>
using namespace std;
struct P{int x, y;}p[18];
bool in_one_line(P a, P b, P c) {return (b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y);}
int n, dp[1 << 16];
int line[18][18];
int dfs(int s)
{
if(dp[s] != 100) return dp[s];
int cnt = 0;
for(int i = 0; i < n; i++)
if(s & (1 << i)) cnt++;
if(cnt == 0) return dp[s] = 0;
if(cnt <= 2) return dp[s] = 1;
for(int i = 0; i < n; i++)
{
if(!(s & (1 << i))) continue;
for(int j = i + 1; j < n; j++)
{
if(!(s & (1 << j))) continue;
dp[s] = min(dp[s], dfs( s^(s&line[i][j]) ) + 1);
}
break;
}
return dp[s];
}
int main()
{
int T;
scanf("%d", &T);
for(int ca = 1; ca <= T; ca++)
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d%d", &p[i].x, &p[i].y);
for(int i = 0; i < n; i++)
for(int j = i + 1; j < n; j++)
{
line[i][j] = (1 << i) + (1 << j);
for(int k = 0; k < n; k++)
if(in_one_line(p[i], p[j], p[k]))
line[i][j] |= (1 << k);
}
for(int s = 0; s < (1 << n); s++)
dp[s] = 100;
printf("Case %d: %d\n", ca, dfs((1 << n) - 1));
}
return 0;
}