P1526 [NOI2003] 智破连环阵题解
__vector__ · · 个人记录
难度:\color{black}\text{NOI/NOI+/CTSC}
题面:
不需要解释
题目分析:
注意,第
可以开个数组 attack[maxn][maxn],记录第
for(reg int i=1;i<=n;i++)
{
for(reg int j=1;j<=m;j++)
{
if(k*k>=((zd[i].x-wq[j].x)*(zd[i].x-wq[j].x)+(zd[i].y-wq[j].y)*(zd[i].y-wq[j].y)))//k*k省去开根运算
{
attack[i][j]=1;
}
}
}
然后跑个dp求解第
for(reg int i=1;i<=n;i++)
{
for(reg int j=m;j>0;j--)
{
if(!attack[i][j])continue;
max_attack[i][j]=max(j,max_attack[i][j+1]);
}
}
再跑个dp求解从第
for(reg int i=m;i>0;i--)
{//dp
for(reg int j=1;j<=n;j++)
{
if(!attack[j][i])continue;
min_zd[i]=min(min_zd[i],min_zd[max_attack[j][i]+1]+1);
}//当前炸起最远能炸到max_attack[j][i],加上1就到了下一个炸弹所对应的武器区间。
//所以min_zd[max_attack[j][i]+1]就是下一个炸弹对应的武器区间炸起所需要的最少炸弹。
//min_zd[max_attack[j][i]+1]还要加上1是因为要考虑到min_zd[max_attack[j][i]+1]是后面的武器区间,
//还需要算上当前炸弹。
}
然后定义一个数组wcpd[maxn][maxn],代表第 vis[maxn][maxn],用于匈牙利算法
然后就是dfs了,定义:
void dfs(int x,int qj);//到了第x个武器,分了qj个区间
然后定义一个数组huisu,用于暂时存储
然后从
bool xylsf(int x)//匈牙利算法
{
for(int i=1;i<=n;i++)
{
if(wcpd[i][x]&&!vis[i])
{
vis[i]=1;
if(!dy[i]||xylsf(dy[i]))
{
dy[i]=x;
return 1;
}
}
}
return 0;
}
void dfs(int x,int qj)//到了第x个武器,分了qj个区间
{
if(qj+min_zd[x]>=ans)return;//剪枝
if(x>m)
{
ans=qj;
return;
}
int huisu[maxn];//用于回溯
for(reg int i=x;i<=m;i++)
{
for(reg int j=1;j<=n;j++)
{
huisu[j]=dy[j];
if(attack[j][x]&&max_attack[j][x]>=i)
{
wcpd[j][qj+1]=1;//j控制了qj++的武器区间
}
}
memset(vis,0,sizeof(vis));
if(xylsf(qj+1))dfs(i+1,qj+1);
//接下来是回溯
for(reg int j=1;j<=n;j++)
{
dy[j]=huisu[j];
if(attack[j][x]&&max_attack[j][x]>=i)
{
wcpd[j][qj+1]=0;//j控制了qj++的武器区间
}
}
}
}
题目分析完毕
\color{green}\text{AC代码:}
#include <bits/stdc++.h>
using namespace std;
#define reg register
int n,m,k;
const int maxn=105;
bool attack[maxn][maxn];//第i个炸弹是否能炸掉第j个武器
int max_attack[maxn][maxn];//第i个炸弹进行攻击,从第j个武器开始炸能攻击到的最大编号武器
int min_zd[maxn];//从第i个开始炸起,最少要用多少炸弹才能炸掉最后的武器
int ans;
int dy[maxn];//一个炸弹配对了那些
bool wcpd[maxn][maxn];
bool vis[maxn];
struct Wq//武器
{
int x,y;
}wq[maxn];
struct Zd//炸弹
{
int x,y;
}zd[maxn];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
bool xylsf(int x)//匈牙利算法
{
for(int i=1;i<=n;i++)
{
if(wcpd[i][x]&&!vis[i])
{
vis[i]=1;
if(!dy[i]||xylsf(dy[i]))
{
dy[i]=x;
return 1;
}
}
}
return 0;
}
void dfs(int x,int qj)//到了第x个武器,分了qj个区间
{
if(qj+min_zd[x]>=ans)return;//剪枝
if(x>m)
{
ans=qj;
return;
}
int huisu[maxn];//用于回溯
for(reg int i=x;i<=m;i++)
{
for(reg int j=1;j<=n;j++)
{
huisu[j]=dy[j];
if(attack[j][x]&&max_attack[j][x]>=i)
{
wcpd[j][qj+1]=1;//j控制了qj++的武器区间
}
}
memset(vis,0,sizeof(vis));
if(xylsf(qj+1))dfs(i+1,qj+1);
//接下来是回溯
for(reg int j=1;j<=n;j++)
{
dy[j]=huisu[j];
if(attack[j][x]&&max_attack[j][x]>=i)
{
wcpd[j][qj+1]=0;//j控制了qj++的武器区间
}
}
}
}
inline void indata()
{
m=read();
n=read();
k=read();
for(reg int i=1;i<=m;i++)
{
wq[i].x=read();
wq[i].y=read();
}
for(reg int i=1;i<=n;i++)
{
zd[i].x=read();
zd[i].y=read();
}
}
inline void init()
{
for(reg int i=1;i<=n;i++)
{
for(reg int j=1;j<=m;j++)
{
if(k*k>=((zd[i].x-wq[j].x)*(zd[i].x-wq[j].x)+(zd[i].y-wq[j].y)*(zd[i].y-wq[j].y)))//k*k省去开根运算
{
attack[i][j]=1;
}
}
}
for(reg int i=1;i<=n;i++)
{
for(reg int j=m;j>0;j--)
{
if(!attack[i][j])continue;
max_attack[i][j]=max(j,max_attack[i][j+1]);
}
}
for(reg int i=1;i<=m;i++)min_zd[i]=0x3f3f3f3f;
for(reg int i=m;i>0;i--)
{//dp
for(reg int j=1;j<=n;j++)
{
if(!attack[j][i])continue;
min_zd[i]=min(min_zd[i],min_zd[max_attack[j][i]+1]+1);
}//当前炸起最远能炸到max_attack[j][i],加上1就到了下一个炸弹所对应的武器区间。
//所以min_zd[max_attack[j][i]+1]就是下一个炸弹对应的武器区间炸起所需要的最少炸弹。
//min_zd[max_attack[j][i]+1]还要加上1是因为要考虑到min_zd[max_attack[j][i]+1]是后面的武器区间,
//还需要算上当前炸弹。
}
ans=n;
}
int main()
{
indata();
init();
dfs(1,0);
printf("%d",ans);
return 0;
}