狀態DP,以dp[i]記錄砍掉的樹的狀態爲 i 時的最少發射激光數,對於每個狀態考慮將當前狀態下未被砍的樹中選一顆來砍,則考慮兩種情況,一個是這顆樹需要單獨一束激光來砍,另一個就是這棵樹和其他已經被砍的某一顆樹形成一條線,而在這條線上的點都可以被砍掉,所以求砍這個方向的前一個狀態時需要將在這條線上的點都減掉。
具體說明看代碼:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
//typedef __int64 int64;
typedef long long ll;
#define M 100005
#define max_inf 0x7f7f7f7f
#define min_inf 0x80808080
#define mod 1000000007
int n , m , x[25] , y[25];
int dp[1<<22];
bool Judge(int k)//判斷當前狀態砍掉的樹時多少棵樹
{
int num = 0;
while (k)
{
num += (k&1);
k >>= 1;
}
return m <= num;
}
void Solve()
{
int i , j , k , l , up = 1<<n , tp[25] , ans = max_inf;
memset(dp , max_inf , sizeof dp);
dp[0] = 0;
for (i = 0 ; i < up ; i++)
{
for (j = 0 ; j < n ; j++)
{
if (i & (1<<j))continue;//若第j顆樹已經在狀態i中,則繼續
int next = i+(1<<j);//砍掉這棵樹後的下一個狀態
dp[next] = min(dp[next],dp[i]+1);
int temp = i , cnt = 0;
for (k = 0 ; k < n ; k++)//記錄狀態i裏,被砍掉的樹
{
if (temp&1)tp[cnt++] = k;
temp >>= 1;
}
for (k = 0 ; k < cnt ; k++)//確定一個方向
{
temp = i;
for (l = 0 ; l < cnt ; l++)
{
//將這個方向上的點都減掉
if ( (y[j]-y[tp[k]])*(x[tp[l]]-x[j]) == (x[j]-x[tp[k]])*(y[tp[l]]-y[j]) ) temp -= (1<<tp[l]);
}
dp[next] = min(dp[next] , dp[temp]+1);
}
}
if (Judge(i))ans = min(ans,dp[i]);
}
printf("%d\n",ans);
}
int main()
{
int t , i , tcase = 1;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
for (i = 0 ; i < n ; i++)scanf("%d%d",x+i,y+i);
if (tcase > 1)printf("\n");
printf("Case #%d:\n",tcase++);
Solve();
}
return 0;
}