Noip2010普及组

T3:

经过 11 年的韬光养晦,某国研发出了一种新的导弹拦截系统,凡是与它的距离不超过其工作半径的导弹都能够被它成功拦截。当工作半径为 0 时,则能够拦截与它位置恰好相同的导弹。但该导弹拦截系统也存在这样的缺陷:每套系统每天只能设定一次工作半径。而当天的使用代价,就是所有系统工作半径的平方和。某天,雷达捕捉到敌国的导弹来袭。由于该系统尚处于试验阶段,所以只有两套系统投入工作。如果现在的要求是拦截所有的导弹,请计算这一天的最小使用代价.

输入
第一行包含 4 个整数x1、y1、x2、y2,每两个整数之间用一个空格隔开,表示这两套导弹拦截系统的坐标分别为(x1, y1)、(x2, y2)。 
第二行包含 1 个整数 N,表示有 N颗导弹。接下来 N行,每行两个整数 x、y,中间用一个空格隔开,表示一颗导弹的坐标(x, y)。不同导弹的坐标可能相同。 

输出
输出只有一行,包含一个整数,即当天的最小使用代价。 

【提示】 
两个点(x1, y1)、(x2, y2)之间距离的平方是(x1− x2)^2+(y1−y2)^2.两套系统工作半径 r1、r2的平方和,是指 r1、r2 分别取平方后再求和,即 r1^2+r2^2.


样例输入
0 0 10 0 

-3 3 
10 0

样例输出
18


数据范围限制
提示
【样例 1 说明】 
样例 1 中要拦截所有导弹,在满足最小使用代价的前提下,两套系统工作半径的平方分别为 18 和 0。 

【输入输出样例 2】 
输入:
0 0 6 0 
5
-4 -2

-2 3

4 0
6 -2

9 1

输出:
30 

【样例 2 说明】 
样例中的导弹拦截系统和导弹所在的位置如下图所示。要拦截所有导弹,在满足最小使用代价的前提下,两套系统工作半径的平方分别为 20 和 10。


【数据范围】
对于 10%的数据,N = 1 
对于 20%的数据,1 ≤ N ≤ 2 
对于 40%的数据,1 ≤ N ≤ 100 
对于 70%的数据,1 ≤ N ≤ 1000 
对于 100%的数据,1 ≤ N ≤ 100000,且所有坐标分量的绝对值都不超过 1000。



这道题是贪心,即一个搜索范围扩大,另一个搜索范围减少,一个捕前i个的最大值,一个捕后i个的最大值,两数结合得出的最小值即为最终的answer。

var
        a,c:array[1..100000]of longint;
        i,n,x,y,x1,x2,y1,y2,tot,ans:longint;
function max(x,y:LOngint):LOngint;
begin
        if x>y then exit(x) else exit(y);
end;
function min(x,y:LOngint):LOngint;
begin
        if x>y then exit(y) else exit(x);
end;
procedure sort(l,r:longint);
var
        i,j,p,mid:longint;
begin
        i:=l; j:=r;
        mid:=c[(l+r) div 2];
        while i<j do
        begin
                while c[i]>mid do inc(i);
                while c[j]<mid do dec(j);
                if i<=j then
                begin
                        p:=a[i]; a[i]:=a[j]; a[j]:=p;
                        p:=c[i]; c[i]:=c[j]; c[j]:=p;
                        inc(i); dec(j);
                end;
        end;
        if i<r then sort(i,r);
        if j>l then sort(l,j);
end;

begin
        assign(input,'missile.in'); reset(input);
        assign(output,'missile.out'); rewrite(output);

        readln(x1,y1,x2,y2);
        readln(n);
        for i:=1 to n do
        begin
                readln(x,y);
                a[i]:=sqr(x-x2)+sqr(y-y2);
                c[i]:=sqr(x-x1)+sqr(y-y1);
        end;

        sort(1,n);

        ans:=c[1];
        for i:=2 to n do
        begin
                tot:=max(tot,a[i-1]);
                ans:=min(ans,c[i]+tot);
        end;
        writeln(ans);

        close(input); close(output);
end.



T4:

第四道题因为题目太长,这里就不复制了,题意就是:在一个数列中,每个数与其他数组合都会产生一个值,然后你每次可以选一个数,选完到下一个人选,以此类推,你怎么选可以使得最后你选的数产生一个最大的默契值。“下一个人”的选法一定是:判断当前没被选过的哪个数与“你”选的数产生的默契值最高,则选这个数。


样例输入
6
5 28 16 29 27
23 3 20 1
8 32 26
33 11
12

样例输出
1
32


数据范围限制
提示
【输入输出样例说明】 
首先小涵拿走 5 号武将;计算机发现 5 号武将和剩下武将中的 4 号默契值最高,于是拿走 4 号;小涵接着拿走 3 号;计算机发现 3、5 号武将之一和剩下的武将配对的所有组合中,5 号和1 号默契值最高,于是拿走 1号;小涵接着拿走 2 号;计算机最后拿走 6 号。在小涵手里的 2,3,5 号武将中,3 号和 5 号配合最好,默契值为 32,而计算机能推出的最好组合为 1 号和 6 号,默契值为 27。结果为小涵胜,并且这个组合是小涵用尽所有方法能取到的最好组合。 


这道题目其实仔细分析一下,一定是先取的人赢,因为不管下一个人怎么选,虽然不能拿到最大值,但是次大值一定是先取的人,所以后取的人一定是输的。再进一步分析,我们可以得出,其实只要选一轮就可以得出结果了,因为题目最后是求一个最大值,并不是选的总和,所以只用进行一轮的选择来判断答案是什么就行了。那么如何得出答案呢?我们知道,当前不管你选哪个数,你不可能选到一个默契值最高的数,但是也因此你必定可以选到一个默契值次高的数,也就是你需要找出i与j组合产生的默契值是i这一行中次大的,同时i<>j就行了,也是一个贪心算法:

var
        a,c:array[1..100000]of longint;
        i,n,x,y,x1,x2,y1,y2,tot,ans:longint;
function max(x,y:LOngint):LOngint;
begin
        if x>y then exit(x) else exit(y);
end;
function min(x,y:LOngint):LOngint;
begin
        if x>y then exit(y) else exit(x);
end;
procedure sort(l,r:longint);
var
        i,j,p,mid:longint;
begin
        i:=l; j:=r;
        mid:=c[(l+r) div 2];
        while i<j do
        begin
                while c[i]>mid do inc(i);
                while c[j]<mid do dec(j);
                if i<=j then
                begin
                        p:=a[i]; a[i]:=a[j]; a[j]:=p;
                        p:=c[i]; c[i]:=c[j]; c[j]:=p;
                        inc(i); dec(j);
                end;
        end;
        if i<r then sort(i,r);
        if j>l then sort(l,j);
end;

begin
        assign(input,'missile.in'); reset(input);
        assign(output,'missile.out'); rewrite(output);

        readln(x1,y1,x2,y2);
        readln(n);
        for i:=1 to n do
        begin
                readln(x,y);
                a[i]:=sqr(x-x2)+sqr(y-y2);
                c[i]:=sqr(x-x1)+sqr(y-y1);
        end;

        sort(1,n);

        ans:=c[1];
        for i:=2 to n do
        begin
                tot:=max(tot,a[i-1]);
                ans:=min(ans,c[i]+tot);
        end;
        writeln(ans);

        close(input); close(output);
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值