状压并不难,记录一下现在经过点的状态和最后一个经过的点即可。
但是有个限制条件是不能直接经过一个没经过的点。
如果直接做的话就时间复杂度2nn3,卡常+O2也过不了啊。。。Orz
所以预处理出来经过某一对点中间必然经过的一些点。
然后dp的时候就直接判断一下就行了。
时间复杂度可以降一个n然后就A了。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cmath> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; short n; const int mod=100000007; struct Node {short x,y;}p[25]; int f[1<<20][21],ans,mst[21][21]; bool cmp(Node a,Node b) {return a.x==b.x?a.y<b.y:a.x<b.x;} short ck(short a,short b,short c) { short xa=p[a].x,ya=p[a].y,xb=p[b].x,yb=p[b].y,xc=p[c].x,yc=p[c].y; if((max(xa,xb)>=xc&&min(xa,xb)<=xc&&max(ya,yb)>=yc&&min(ya,yb)<=yc)) return (float)(xa-xb)/(ya-yb)==(float)(xa-xc)/(ya-yc); return 0; } int main() { cin>>n; for(short i=0;i<n;i++) cin>>p[i].x>>p[i].y; for(short i=0;i<n;i++) f[1<<i][i+1]=1; sort(p,p+n,cmp); for(short i=0;i<n;i++) { for(short j=0;j<n;j++) { if(i^j) for(short k=0;k<n;k++) if((i^j)&&(i^k)&&(j^k)) mst[i][j]|=ck(i,j,k)<<k; } } register int i;register short j,k; for(i=1;i<(1<<n);i++) { for(j=0;j<n;j++) { if((1<<j)&i) for(k=0;k<n;k++) { if(!((1<<k)&i)) { if((i&mst[j][k])==mst[j][k]) {f[i|(1<<k)][k+1]+=f[i][j+1];if(f[i|(1<<k)][k+1]>=mod) f[i|(1<<k)][k+1]-=mod;} } } } } for(i=15;i<1<<n;i++) { if(__builtin_popcount(i)<4) continue; for(j=1;j<=n;j++) {if((1<<j-1)&i)ans=(ans+f[i][j]);if(ans>=mod) ans-=mod;} } cout<<ans<<endl; return 0; }