CQOI2018 解锁屏幕
题目链接:戳我
其实只有开了O2才能A.......
就是我们看到n的范围这么小,就想到状压DP。然后我们设状态dp[i]表示状态为i的(二进制表示该点选或者不选),方案数有多少个。
但是我们发现因为还要枚举转移下一个点,所以我们还要记录一下最后的那个点是什么。
于是我们修改状态为\(dp[i][j]\)表示当前状态为i,最后一个点的编号为j的,方案数有多少个。
然后\(O(2^n*n^2)\).......吧
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define MAXN 21
#define eps 1e-7
#define mod 100000007
using namespace std;
int n,m,ans;
int dp[1<<MAXN][MAXN];
struct Node{int x,y;}node[MAXN];
vector<int>mid[MAXN][MAXN];
inline bool cmp(struct Node a,struct Node b)
{
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
inline bool check(int a,int b,int c)
{
double k1,k2;
if(node[a].x==node[b].x&&node[a].x==node[c].x) return true;
else if(node[a].x==node[c].x||node[a].x==node[b].x) return false;
k1=1.0*(node[a].y-node[b].y)/(node[a].x-node[b].x);
k2=1.0*(node[a].y-node[c].y)/(node[a].x-node[c].x);
if(fabs(k1-k2)<eps) return true;
else return false;
}
inline bool more_than_4(int x)
{
int cur_ans=0;
for(int i=0;i<n;i++)
if(x&(1<<i))
cur_ans++;
return cur_ans>=4;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&node[i].x,&node[i].y);
sort(&node[1],&node[n+1],cmp);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
for(int k=i+1;k<=j-1;k++)
if(check(i,j,k)==true)
mid[i][j].push_back(k);
for(int i=1;i<=n;i++) dp[1<<(i-1)][i]=1;
for(int i=1;i<(1<<n);i++)
{
for(int j=1;j<=n;j++)
{
if((i>>(j-1))&1) continue;
int s=(i|(1<<(j-1)));
for(int k=1;k<=n;k++)
{
if(k==j) continue;
int x=min(j,k),y=max(j,k),flag=0;
for(int t=0;t<mid[x][y].size();t++)
if(!((i>>(mid[x][y][t]-1))&1)){flag=1;break;}
if(flag==1) continue;
dp[s][j]=(dp[s][j]+dp[i][k])%mod;
}
}
}
for(int i=1;i<(1<<n);i++)
if(more_than_4(i))
for(int j=1;j<=n;j++)
ans=(ans+dp[i][j])%mod;
printf("%d\n",ans);
return 0;
}