light oj 1018 状态压缩DP

http://www.lightoj.com/volume_showproblem.php?problem=1018

题意:给你n个坐标点(n<=16),问你最少划几条线能穿过所有的点


做法:

1:n很小,目测应该是状态压缩DP

2:划一次线至少会经过两个点(要不然就太亏了),所以可以枚举两个点,预处理两个点所在直线所经过的点集的状态(dp[i][j])

3:然后就可以记忆化搜索了,每次从s状态中枚举两个点所在的直线去掉直线上所有的点

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int inf = ~0u>>2;
int dp[20][20];
int n;
int f[1<<16];
struct point {
    int x,y;
}in[20];
int cross(point a,point b,point c){return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);}
bool col(int a,int b,int c){
    return cross(in[a],in[b],in[c])==0;
}
void init(){
    memset(dp,0,sizeof(dp));
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++){
            for(int k=0;k<n;k++){
                if(col(i,j,k)){
                    dp[i][j]+=(1<<k);
                }
            }
        }
    }
}
int DP(int s)
{
    if(f[s]!=inf) return f[s];
    int cnt=0;
    for(int i=0;i<n;i++) if(s&(1<<i)) cnt++;
    if(cnt==0) return f[s]=0;
    if(cnt<=2) return f[s]=1;
    for(int i=0;i<n;i++)if(s&(1<<i))
    {
        for(int j=i+1;j<n;j++)if(s&(1<<j))
        {
            f[s]=min(f[s],DP(s-(s&dp[i][j]))+1);
        }
        break;//else TLE
    }
    return f[s];
}
int main()
{
    int t,ca=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&in[i].x,&in[i].y);
        }
        init();
        fill(f,f+(1<<16),inf);
        printf("Case %d: %d\n",ca++,DP((1<<n)-1));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值