題意: 給平面上n個點(n<=16),問最少幾條線能把所有點覆蓋。
解法: 16個點,又是在DP分類,沒怎麼想就是狀態壓縮DP了。先預先處理點兩兩之間組成的直線經過的點集line[i][j]。然後每次枚舉兩個點就可以了。
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define REP(i,a,b) for(int i=(a); i<(b); i++)#define FOR(i,a,b) for(int i=(a); i<=(b); i++)const int INF = ~0u>>1;typedef long long lld;struct POINT { int x, y;}node[20];int n;int line[20][20];int dp[(1<<16) + 10];int cnt;int judge(POINT a, POINT b, POINT c) { return (a.y-b.y)*(c.x-a.x) == (a.x-b.x)*(c.y-a.y);}int dfs(int s) { int i,j; if(dp[s] != INF) return dp[s]; cnt = 0; for(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(i=0; i<n; i++) { if((s&(1<<i)) == 0) continue; for(j=i+1; j<n; j++) { if((s&(1<<j)) == 0) continue; dp[s] = min(dp[s], dfs(s^(s&line[i][j]))+1); } break; } return dp[s];}void gao() { scanf("%d", &n); REP(i,0,n) scanf("%d%d", &node[i].x, &node[i].y); clr(line,0); 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(judge(node[i],node[j],node[k])) line[i][j] |= (1<<k); } } } int all = (1<<n) - 1; for(int i=0; i<=all; i++) dp[i] = INF; printf("%d\n", dfs(all));}int main() { int cas, ca= 0 ; scanf("%d", &cas); while(cas--) { printf("Case %d: ", ++ca); gao(); } return 0;}