题目链接: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; }