题目链接:http://lightoj.com/volume_showproblem.php?problem=1018
题意分析:二分平面上有N个点,现在有一把可沿着任何方向走的刷子可以刷去这些点,问最少需要刷出多少条直线可以把点刷完?
解题思路:这个是个比较典型的状压dp问题,首先N不大,可以用二进制表示每个点的状态,1表示还没有被刷掉,0表示被刷掉了。
那么状态from中有num个1,如果num<=2显然是为1的,0的话就是0。那么我们想法刷掉from中的点,这样就可以得到状态to,比如我们刷掉的是i,j两个点,显然,这两个点中间的点也会被刷掉,用line[i][j]表示这两点之间点的状态,然后对应着把from中对应line[i][j]的点变成0,这样就得到状态to了,dp[from]=min(dp[from],dp[from|line[i][j]]+1)。
当然也可以写成记忆画搜索的。。。
感悟:由于开始没有想到line这个东西,直接枚举三点一线的情况来进行状态转移,TLE到爆,本地跑随机的120组数据就要花10s以上的时间,更不要说题目给的3000这么大的数了。
/*****************************************
Author :Crazy_AC(JamesQi)
Time :2015
File Name :
*****************************************/
// #pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <climits>
using namespace std;
#define MEM(x,y) memset(x, y,sizeof x)
#define pk push_back
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> ii;
const double eps = 1e-10;
const int inf = 1 << 30;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 17;
int line[maxn][maxn];
int dp[1<<maxn];
int t,icase=0;
int n;
int x[maxn],y[maxn];
bool check(int i,int j,int k){
ii A(x[i]-x[j],y[i]-y[j]);
ii B(x[i]-x[k],y[i]-y[k]);
return A.first*B.second-A.second*B.first==0;
}
inline void Initation(){
for (int i=0;i<n;++i){
for(int j=i+1;j<n;++j){
for (int k=0;k<n;++k){
if (check(i,j,k))
line[i][j] |= 1<<k;
}
line[j][i]=line[i][j];
}
}
}
// int dfs(int cur){
// if (dp[cur] < INF) return dp[cur];
// int num = __builtin_popcount(cur);
// if (num <= 2) return 1;
// int i=0;
// while(!(cur&(1<<i)))i++;
// for(int j=i+1;j<n;++j){
// if (cur&(1<<j)){
// dp[cur]=min(dp[cur],dfs(cur &(~line[i][j]))+1);
// }
// }
// return dp[cur];
// }
vector<int> G[1 << maxn];
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
for (int i=0;i<70000;++i){
for(int j=0;j<maxn;++j){
if ((i&(1<<j))==0)
G[i].push_back(j);
}
}
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(line, 0,sizeof line);
memset(dp,INF,sizeof dp);
dp[0]=0;
for (int i=0;i<n;++i){
scanf("%d%d",&x[i],&y[i]);
line[i][i] = (1<<i);
}
Initation();
cout << "Case " << ++icase << ": ";
int up=1<<n;
for (int i=0;i<up;++i){
int xx=G[i][0];
for (int j=0;j<G[i].size();++j){
int yy=G[i][j];
dp[i|line[xx][yy]]=min(dp[i|line[xx][yy]],dp[i]+1);
}
}
cout << dp[up-1] <<endl;
// cout << dfs((1<<n)-1) << endl;
}
return 0;
}