bzoj 4622: [NOI 2003] 智破连环阵

感觉和之前百度之星那题是差不多套路吧,都是搜索套上一个二分图优化
很容易知道,每个炸弹都是炸掉连续的一段
然后你就搜索一下哪些段
然后二分图判定一下
注意要边搜索边判定,这样是一个强大的可行性剪枝,然后就可以跑过去了
其实在分段的时候也有技巧,就是肯定是在每个炸弹端点的时候分,但是我比较懒,没有加,反正也过了
因为这题数据随机

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=105;
int m,n,k;//武器   炸弹   攻击范围
struct qq
{
    int x,y;
}a[N],b[N];
int dis (qq a,qq b)
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
int ans;
bool ok[N][N];
bool s[N][N];//能不能炸到
int tt[N][N];//这个点如果是true的话下一个是什么
int f[N];//这个炸弹配对了谁 
bool vis[N];
bool find (int x)
{
    for (int u=1;u<=n;u++)
        if (ok[u][x]&&vis[u])
        {
            vis[u]=false;
            if (f[u]==-1||find(f[u]))
            {
                f[u]=x;
                return true;
            }
        }
    return false;
}
int dist[N];
void dfs (int y,int z)//划分到第几个点   划分了多少段 
{
    if (z+dist[y]>=ans) return ;
    if (y>m)
    {
        ans=z;
        return ;
    }
    int ff[N];
    for (int i=y;i<=m;i++)//这一段划在哪里 
    {
        for (int j=1;j<=n;j++)
        {
            ff[j]=f[j];
            if (s[j][y]==true&&tt[j][y]>=i)
                ok[j][z+1]=true;
        }
        memset(vis,true,sizeof(vis));
        if (find(z+1)) dfs(i+1,z+1);
        for (int j=1;j<=n;j++)
        {
            f[j]=ff[j];
            if (s[j][y]==true&&tt[j][y]>=i)
                ok[j][z+1]=false;
        }
    }
}
int main()
{
    scanf("%d%d%d",&m,&n,&k);
    for (int u=1;u<=m;u++)  scanf("%d%d",&a[u].x,&a[u].y);
    for (int u=1;u<=n;u++)  scanf("%d%d",&b[u].x,&b[u].y);
    memset(s,false,sizeof(s));
    for (int u=1;u<=n;u++)
        for (int i=1;i<=m;i++)
            if (dis(b[u],a[i])<=k*k)
                s[u][i]=true;
    for (int u=1;u<=n;u++)
        for (int i=m;i>=1;i--)
        {
            if (s[u][i]==false) continue;
            tt[u][i]=max(i,tt[u][i+1]);
        }
    memset(dist,127,sizeof(dist));
    dist[m+1]=0;
    for (int u=m;u>=1;u--)
    {
        for (int i=1;i<=n;i++)
        {
            if (s[i][u]==false) continue;
            dist[u]=min(dist[u],dist[tt[i][u]+1]+1);
        }
    }
    memset(f,-1,sizeof(f));
    memset(ok,false,sizeof(ok));
    ans=n;
    dfs(1,0);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值