Ural Championship 2013 H E-Lite

E-Lite

Time limit: 2.0 second Memory limit: 64 MB

The developers of the e-Lite game made it possible for each player to
adjust the parameters of his spaceship as he wants. Initially each
player has a spaceship with n empty slots on its body. The player can
either put up a jet engine in a slot or use this slot for a cargo
module. Each slot can be used for an engine of a specific type, i.e.,
with a certain direction and power. The spaceship’s controlling system
is designed so that all the engines can be either simultaneously
turned on at full power or simultaneously turned off. If the engines
accelerate the spaceship in different directions, it can happen that
the installation of all the engines imparts to the spaceship a smaller
acceleration than the installation of only a part of them. That is why
a player should think where to install engines to get the maximum
acceleration.

Input
The first line contains the number n (1 ≤ n ≤ 1 000) of empty slots on the spaceship’s body. The i-th of the following n lines contains integers xi and yi (−106 ≤ xi, yi ≤ 106), which are the coordinates of the vector of acceleration imparted to the ship by the i-th engine.

Output
For each i from 1 to n, output in a separate line the value of the maximum acceleration of a ship with exactly i engines. The absolute or relative error of each answer should not exceed 10−6. Sample

input
3
3 -2
-3 -2
0 4

output
4.000000
4.000000
0.000000

题目简述:有n个向量,求k(k=1,2…n)个向量合成的最长向量长度。

对于一个答案向量,合成它的向量必然是在答案向量上投影最长的k个向量。可以枚举答案向量方向,将已知向量关于在答案向量的投影排序,取前k个向量和更新答案ans[k]。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;

typedef double db;

const int maxn = 1e3 + 5;
const int m = 1e4;
const db pi = acos(-1.0);

int n, rnk[maxn];
db vx[maxn], vy[maxn], ans[maxn], ux, uy;

bool cmp(int i, int j){

    return vx[i] * ux + vy[i] * uy > vx[j] * ux + vy[j] * uy;
}

int main(){

    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%lf%lf", &vx[i], &vy[i]);
    for(int i = 1; i <= n; ++i) rnk[i] = i;
    for(int i = 1; i <= m; ++i){
        db ang = 2.00 * pi / m * (i - 1);
        ux = cos(ang);
        uy = sin(ang);
        sort(rnk + 1, rnk + n + 1, cmp);
        db tx = 0, ty = 0;
        for(int j = 1; j <= n; ++j){
            tx += vx[rnk[j]];
            ty += vy[rnk[j]];
            ans[j] = max(ans[j], tx * tx + ty * ty);
        }
    }
    for(int i = 1; i <= n; ++i) printf("%.10f\n", sqrt(ans[i]));

    return 0;
}
//Thanks to XYT

有个更有正确性保证但代码复杂度更高的方法:将两两向量和的垂直平分线列举出来作为决策点,开始假设答案向量在x轴正半轴方向,对于答案向量每个给定向量都有一个贡献的排名rank[i],每当转过一个决策点,决策点的两个向量rank会交换,不断维护这个rank和当前答案,取最优便可得到最后答案。
麻烦之处就在于重合向量的处理,具体处理参见以下程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cassert>
#include<map>
#define rank rrrr
using namespace std;
typedef long double LD;
const int N = 1010;
const LD pi = acos(-1);
const LD eps = 1e-12;
map<pair<int,int>,int >mm;
int n, xx[N], yy[N];
LD Sqrt(LD x){
    return x < eps ? 0 : sqrt(x);
}
struct Point{
    LD x,y;
    int num;
    Point(){}
    Point(LD x, LD y):x(x),y(y){}
    LD length(){
        return x * x + y * y;
    }
}p[N];
Point operator - (const Point &a, const Point &b){
    return Point(a.x - b.x, a.y - b.y);
}
Point operator + (const Point &a, const Point &b){
    return Point(a.x + b.x, a.y + b.y);
}
Point operator * (const Point &a, const LD &k){
    return Point(a.x * k, a.y * k);
}
LD dot(const Point &a, const Point &b){
    return a.x * b.x + a.y * b.y;
}
int rank[N], id[N];
int cnt[N];
LD key[N];
Point sum[N];
LD ans[N];
struct Node{
    LD ang;
    int p, q;
    Node(){}
    Node(LD ang, int p, int q):ang(ang), p(p), q(q){}
}a[N * N];
bool cmp_angle(const Node &a, const Node &b){
    return a.ang < b.ang;
}
bool cmp_id(const Node &a, const Node &b){
    if(rank[a.p] != rank[b.p]) return rank[a.p] < rank[b.p];
    return rank[a.q] < rank[b.q];
}
void initial_sort(){
    //Point vec(cos(ang), sin(ang));
    for(int i=1;i<=n;i++){
        id[i] = i;
    }
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
        if(!(xx[id[i]] < xx[id[j]] || (xx[id[i]]==xx[id[j]] && yy[id[i]] < yy[id[j]]))){
            swap(id[i], id[j]);
        }
    sum[0] = Point(0,0);
    cnt[0] = 0;
    int now = 0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=p[id[i]].num;j++)
        {
            now++;
            sum[now] = sum[now-1] + p[id[i]];
            ans[now] = sum[now].length();
            rank[id[i]] = i;
        }
        cnt[i] = now;
    }
}
void work(int pre, int la){
    assert(rank[pre] == rank[la] - 1);
    int k = rank[pre];
    int now = cnt[k-1];
    for(int i=1;i<=p[la].num;i++){
        now++;
        sum[now] = sum[now - 1] + p[la];
        ans[now] = max(ans[now], sum[now].length());
    }
    cnt[k] = now;
    for(int i=1;i<=p[pre].num;i++){
        now++;
        sum[now] = sum[now-1] + p[pre];
        ans[now] = max(ans[now], sum[now].length());
    }
    swap(rank[pre], rank[la]);
}
int main(){
    cin>>n;
    int n_bak = n;
    for(int i=1;i<=n;i++){
        scanf("%d%d", &xx[i], &yy[i]);
        p[i] = Point(xx[i],yy[i]);
        mm[make_pair(xx[i],yy[i])] ++;;
    }
    n = 0;
    for(map<pair<int,int>, int>::iterator it = mm.begin();it!=mm.end();it++){
        n++;
        xx[n] = (it -> first).first;
        yy[n] = (it -> first).second;
        p[n] = Point(xx[n], yy[n]);
        p[n].num = it -> second;
    }
    int num = 0;
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
    if(xx[i]!=xx[j] || yy[i] != yy[j])
    {
        a[++num] = Node(atan2(xx[j] - xx[i], yy[i] - yy[j]), j, i);//j is defeated by i
        a[++num] = Node(atan2(-xx[j] + xx[i], -yy[i] + yy[j]), i, j);
    }
    sort(a+1, a+num+1, cmp_angle);
    initial_sort();
    for(int i=1,j;i<=num;i=j){
        for(j = i+1; fabs(a[i].ang - a[j].ang) < eps && j<=num;j++);
        sort(a+i, a+j, cmp_id);
        for(int k=i;k<j;k++){
            work(a[k].p, a[k].q);
        }
    }
    for(int i=1;i<=n_bak;i++)
        printf("%.8f\n",(double)Sqrt(ans[i]));
}
//Thanks to XZJ
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值