基站选址(编程之美2015资格赛)

时间限制:2000ms
单点时限:1000ms
内存限制:256MB

描述

需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上。

网格中有A个用户,每个用户的通讯代价是用户到基站欧几里得距离的平方。

网格中还有B个通讯公司,维护基站的代价是基站到最近的一个通讯公司的路程(路程定义为曼哈顿距离)。

在网格中建立基站的总代价是用户通讯代价的总和加上维护基站的代价,最小总代价。

输入

第一行为一个整数T,表示数据组数。

每组数据第一行为四个整数:N, M, A, B。

接下来的A+B行每行两个整数x, y,代表一个坐标,前A行表示各用户的坐标,后B行表示各通讯公司的坐标。

输出

对于每组数据输出一行"Case #X: Y",X代表数据编号(从1开始),Y代表所求最小代价。

数据范围

1 ≤ T ≤ 20

1 ≤ x ≤ N

1 ≤ y ≤ M

1 ≤ B ≤ 100

小数据

1 ≤ N, M ≤ 100

1 ≤ A ≤ 100

大数据

1 ≤ N, M ≤ 107

1 ≤ A ≤ 1000

样例输入
2
3 3 4 1
1 2
2 1
2 3
3 2
2 2
4 4 4 2
1 2
2 4
3 1
4 3
1 4
1 3
样例输出
Case #1: 4
Case #2: 13
#include <iostream>
#include <limits.h>
#include <malloc.h>

using namespace std;

enum type{base,user};

typedef struct node
{
    int posx;
    int posy;
    type kind;
} node;

int findPostive(int data)
{
    if(data>=0)
        return data;
    else
        return -data;
}

int main()
{
    int num;
    cin>>num;
    int k = 0;
    while(k<num)
    {
        int n,m,a,b;
        cin>>n>>m>>a>>b;
        node *data = new node[a+b];

        for(int i = 0;i < a+b; i++)
        {
            cin>>data[i].posx>>data[i].posy;
            if(i<a)
                data[i].kind = user;
            else
                data[i].kind = base;
        }
        int min = INT_MAX;
        for(int i = 1;i <= n; i++)
            for(int j = 1;j <= m; j++)
            {
                int l;
                int cost = 0;
                for(l = 0;l < a; l++)
                    cost += ((i-data[l].posy)*(i-data[l].posy)+(j-data[l].posx)*(j-data[l].posx));

                int* dis = new int[b];

                for(l = a;l < a+b; l++)
                {
                    dis[l-a] = (findPostive(i-data[l].posy)+findPostive(j-data[l].posx));
                }

                int minvalue = INT_MAX;
                for(int h = 0; h < b; h++)
                    if(dis[h]<minvalue)
                        minvalue = dis[h];
                free(dis);
                cost += minvalue;
                //cout<<cost<<"  hello "<<endl;
                if(cost<min)
                min = cost;
            }
        cout<<"Case #"<<(k+1)<<": "<<min<<endl;
        k++;
        free(data);

    }
}

  由于上面算法是在每个格子中尝试查找,因此效率比较低,会出现超时,并且有可能溢出。

设A个用户的位置,设公司的位置,推出代价函数公式为:

对于变量x和y求导知

令导数为零,知

由于x,y只能取格子中的值,即x,y必须为整数,但是上述公式求出的为浮点数,所以共有4个值可以尝试。

代码如下:

#include <iostream>
#include <vector>
#include <string>
#include <vector>
#include <math.h>
using namespace std;
#define modnum 100007
long long cost=0;
long long maxll=9223372036854775807;
long long mincost=maxll;
long long totalAx=0;
long long totalAxSquare=0;
long long totalAy=0;
long long totalAySquare=0;
vector<long long> bx;
vector<long long> by;
int N,M,A,B;
long long cal(long long totalSquare,long long total,long long x,long long num) 
{
    long long ans=0;
    ans+=totalSquare+num*x*x-2*total*x;
    return ans;
}
long long calAll(long long x,long long y)           
{
    long long ans=maxll;
    for(int i=0;i<bx.size();i++)
    {
        ans=min(ans,abs(bx[i]-x)+abs(by[i]-y));      
    }
    return ans+cal(totalAxSquare,totalAx,x,A)+cal(totalAySquare,totalAy,y,A);   
}
int main()
{
    
    int T=0;
    int Case=0;
    cin>>T;
    while(T)
    {
        bx.clear();
        by.clear();
        totalAx=0;
        totalAxSquare=0;
        totalAy=0;
        totalAySquare=0;
        
        T--;
        Case++;

        
        cin>>N>>M>>A>>B;
        long long ans=maxll;
        long long temp=0;
        for(int i=0;i<A;i++)      
        {
            cin>>temp;
            totalAx+=temp;
            totalAxSquare+=temp*temp;
            cin>>temp;
            totalAy+=temp;
            totalAySquare+=temp*temp;
        }
        for(int i=0;i<B;i++)
        {
            cin>>temp;
            bx.push_back(temp);
            cin>>temp;
            by.push_back(temp);
        }
        long long x,y;
        x=totalAx/A;          
        y=totalAy/A;
        ans=min(ans,calAll(x,y+1));
        ans=min(ans,calAll(x+1,y));
        ans=min(ans,calAll(x+1,y+1));
        ans=min(ans,calAll(x,y));

        cout<<"Case #"<<Case<<": "<<ans<<endl;
    }
}

  代码来自:http://www.cnblogs.com/tjsudys/p/4436433.html

 

posted @ 2015-04-18 11:58  再见,少年  Views(292)  Comments(0Edit  收藏  举报