ZZULIOJ 1730 通信基站 全排列+DFS

题目链接:https://acm.zzuli.edu.cn/zzuliacm/problem.php?id=1730

此题的关键就在于找到基站的辐射半径

枚举在呢些点建基电站,用一个全排列,1代表建,0代表不建。我们把0的点存为y[],1的点存为x[],然后就是从x[]里面选若干个点引半径做圆 来覆盖y[]里面的所有点,只需要求出最小半径和.

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define INF 0x3f3f3f3f
using namespace std;
double a[10],b[10];
int vis[10],n;
double Cs,Cr;
int x[10],y[10];
double bj[10];
int s1,s2;
double sum,minn;
void dfs(int v)
{
    if(v==s1+1)//已经把所有不建基站的地方都覆盖
    {
        double cnt=0;
        for(int i=1; i<=s2; i++)
            cnt+=bj[i];
        sum=min(sum,cnt);
        return;
    }
    for(int i=1; i<=s2; i++)//把s2个建基站的半径扩大来覆盖s1个不建基站的地方
    {
        double dis=sqrt((a[x[i]]-a[y[v]])*(a[x[i]]-a[y[v]])+(b[x[i]]-b[y[v]])*(b[x[i]]-b[y[v]]));//半径
        double ls=bj[i];
        bj[i]=max(bj[i],dis);//注意,选最大的才能全部被覆盖
        dfs(v+1);
        bj[i]=ls;//回溯,这个基站不向外扩展
    }
}
void fun()
{
    s1=0,s2=0;
    for(int i=1; i<=n; i++)
    {
        if(vis[i]==0)
            y[++s1]=i;//不建基站的放在y数组
        else
            x[++s2]=i;//建基站的放在x数组
    }
    if(s1!=0&&s2!=0)// s1==0即全都不建基站,不成立 / s2==0即全都建基站,在minn初始化的时候已经算过
    {
        memset(bj,0,sizeof(bj));//存放基站的辐射半径
        sum=INF; //基站的辐射半径和
        dfs(1);
        minn=min(minn,Cs*s2+Cr*sum);
    }

}
void judge()
{
    //用01的全排列,1表示在该地方建基站,0表示不建
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
            vis[j]=1;
        for(int j=1; j<=i; j++)//有i个建基站的建筑
            vis[j]=0;
        do
        {
            fun();//枚举这i个基站分别建在呢个建筑
        }
        while(next_permutation(vis+1,vis+n+1));
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%lf%lf",&n,&Cs,&Cr);
        for(int i=1; i<=n; i++)
            scanf("%lf%lf",&a[i],&b[i]);
        minn=Cs*n;//每座建筑都建立基站
        judge();
        printf("%.2f\n",minn);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/zch3210/article/details/71158224