Uva 11008 - Antimatter Ray Clearcutting 状态压缩

本文介绍了一个有趣的状压DP算法问题:通过射击砍掉指定数量的树。利用二进制状态压缩来记录每棵树的状态,并通过动态规划算法求解最小射击次数。文章详细解释了解题思路并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天可以说弄了好久,之前没怎么接触状压,不过今天看到一题,其实懂了意思,经感觉有点有趣。。

题意:给出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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值