今天可以说弄了好久,之前没怎么接触状压,不过今天看到一题,其实懂了意思,经感觉有点有趣。。
题意:给出n棵树的位置,要求一个人可以站在任意位置(我以为是固定位置,后面看样列发现是任意的),以任意方用枪打掉m棵树,求最少打的枪数
思路:用二进制表示所有数的状态,如4棵树1111,为0 证明数被砍掉,为1没被砍掉,所以只要计数1的个数少于等于n-m即可 用s[i][j]表示以i,j为两点确定的一条直线,它的值的二进制表示也映射出了这条线上的树的棵树极其状态
ff[S]表示在状态S下最少需要几枪
代码:
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#define lson l,mid,num<<1
#define rson mid+1,r,num<<1|1
using namespace std;
const int M=100005;
#define inf 0x7fffffff
#define M 20
#define Max 1<<18
int x[M],y[M];
int s[M][M];//有第i个点和第j个点连接成的直线
int ff[Max];//表示这个状态最少需要多少把枪
int n,m,c;
int dp(int S)
{
int sum=0,ans;
if(ff[S]!=-1)return ff[S];
for(int i=0; i<n; i++)
if(S&(1<<i))sum++;//存在这个点
if(sum<=c)return ff[S]=0;
if(sum==1)return ff[S]=1;//只存在一个点
ff[S]=inf;
for(int i=0; i<n; i++)
{
if((1<<i) & S)
for(int j=i+1; j<n; j++)
if((1<<j) & S)
{
ans=dp(S&(~s[i][j]))+1;//一枪打掉这条直线上的点
if(ans <ff[S])
ff[S]=ans;
}
}
return ff[S];
}
void solve(int g)
{
int ans;
memset(s,0,sizeof(s));
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
if(i==j)continue;
for(int k=n-1; k>=0; k--)
{
s[i][j]<<=1;
if((y[j]-y[i]) *(x[k]-x[i])==(y[k]-y[i])*(x[j]-x[i]))
s[i][j]++;
}
}
}
c=n-m;
memset(ff,-1,sizeof(ff));
ans=dp((1<<n)-1);//刚开始有2^n-1棵树(状态)
printf("Case #%d:\n%d\n",g,ans);
}
int main()
{
int t,g=0;
scanf("%d",&t);
for(int i=1; i<=t; i++)
{
g++;
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
scanf("%d%d",&x[i],&y[i]);
solve(g);
if(g!=t)
printf("\n");
}
return 0;
}