• [luogu1526]智破连环阵


    (以下在描述复杂度时,认为$n$和$m$相同,因此一律使用$n$)

    称第$i$个炸弹能匹配非空区间$[l,r]$,当且仅当$l$到$r$内所有武器都在$i$攻击范围内,且$r=m$或第$r+1$个武器不在$i$攻击范围内

    (更通俗的表示即在第$l$个武器启动时,使用第$i$个炸弹恰好会攻击到第$r$个武器)

    关于这个区间的匹配,显然可以在$o(n^{2})$的时间内预处理

    下面,将$n$个武器划分为若干个区间,分别表示每一个炸弹所破坏的武器(恰好),爆搜来确定区间划分后,只需判定其是否合法,并用区间数对答案取$min$即可

    关于判定,将第$i$个炸弹向划分出的区间中能匹配的区间连边,判定最大匹配是否等于区间数即可

    暴搜复杂度为$o(n^{2}2^{n})$(即每一个位置是否划分,以及二分图匹配),下面来考虑剪枝——

    剪枝1:求出$[i,n]$这些武器,在允许重复利用炸弹的情况下的最少次数$g_{i}$

    若当前划分出$now$个区间,最后一个区间右端点为$r$,最小答案为$ans$,那么若$now+g_{r+1}ge ans$显然不需要搜索下去,直接返回即可

    关于$g_{i}$的计算,即$g_{i}=min_{炸弹j能匹配区间[i,k]}g_{k+1}+1$(初始$g_{m+1}=0$),复杂度为$o(n^{3})$

    剪枝2:在搜索过程中,每一次划分出一个区间后,即在最终的二分图中新增一个点,根据二分图的过程,直接对其再求一次匹配,当某一次无法匹配直接退出即可

    剪枝3:从后往前枚举右端点,在综合剪枝1和剪枝2的基础上,此优化也有一定的用处

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 105
     4 struct point{
     5     int x,y;
     6 }a[N],b[N];
     7 vector<int>v[N<<1];
     8 int n,m,k,ans,g[N],Vis[N][N][N],mm[N][N<<1],vis[N<<1],match[N<<1];
     9 int sqr(int k){
    10     return k*k;
    11 }
    12 bool check(point x,point y){
    13     return sqr(x.x-y.x)+sqr(x.y-y.y)<=sqr(k);
    14 }
    15 bool dfs(int k){
    16     if (vis[k])return 0;
    17     vis[k]=1;
    18     for(int i=0;i<v[k].size();i++)
    19         if ((!match[v[k][i]])||(dfs(match[v[k][i]]))){
    20             match[k]=v[k][i];
    21             match[v[k][i]]=k;
    22             return 1;
    23         }
    24     return 0;
    25 } 
    26 void dfs(int k,int s){
    27     if (s+g[k]>=ans)return;
    28     if (k>m){
    29         ans=s;
    30         return;
    31     }
    32     memcpy(mm[s],match,sizeof(mm[s]));
    33     for(int i=m;i>=k;i--){
    34         for(int j=1;j<=n;j++)
    35             if (Vis[j][k][i])v[n+s+1].push_back(j);
    36         memset(vis,0,sizeof(vis));
    37         if (dfs(n+s+1)){
    38             dfs(i+1,s+1);
    39             memcpy(match,mm[s],sizeof(mm[s]));
    40         }
    41         for(int j=1;j<=n;j++)
    42             if (Vis[j][k][i])v[n+s+1].pop_back();
    43     }
    44 }
    45 int main(){
    46     scanf("%d%d%d",&m,&n,&k);
    47     for(int i=1;i<=m;i++)scanf("%d%d",&a[i].x,&a[i].y);
    48     for(int i=1;i<=n;i++){
    49         scanf("%d%d",&b[i].x,&b[i].y);
    50         for(int j=1;j<=m;j++){
    51             if (!check(b[i],a[j]))continue;
    52             for(int k=j;k<=m+1;k++)
    53                 if ((k>m)||(!check(b[i],a[k]))){
    54                     Vis[i][j][k-1]=1;
    55                     break;
    56                 }
    57         }
    58     }
    59     g[m+1]=0;
    60     for(int i=m;i;i--){
    61         g[i]=n;
    62         for(int j=1;j<=n;j++)
    63             for(int k=i;k<=m;k++)
    64                 if (Vis[j][i][k])g[i]=min(g[i],g[k+1]+1);
    65     }
    66     ans=n;
    67     dfs(1,0);
    68     printf("%d",ans);
    69 }
    View Code
  • 相关阅读:
    input 去除边框
    分页封装
    python后端继承序列化,不同访问形式返回不同结果
    解决vue前端不显示自定义字段
    Vue 获取后端多对多关联表信息
    vue 前段增删改查代码规范
    前段增删改查的应用
    ant-design-vue基础
    python 后端 数据库的创表及增删改查 简单使用
    配置Uwsgi+Nginx+Django+Vue
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14729612.html
Copyright © 2020-2023  润新知