【解题报告】2014ACM/ICPC亚洲区广州站

题目链接


A.Dogs’ Candies(HDU5127)

思路

本题本应该是用比较复杂的方法解决,但是由于题目给的时间比较多,因此可以暴力水过去。我们将操作分成两类,一类是插入删除类,另一类是查找类。假设这两类运算的数量分别为 n1 n2 ,那么插入删除的复杂度为 O(n1) ,而查找的复杂度为 O(n1×n2) 。因为计算糖果的美味值的系数是动态给出的,所以查找的复杂度是没办法优化了。但是我们仍可以通过用链表维护糖果来将插入和删除变成常数复杂度操作。那么真正耗时的就是查找了,它有多耗时呢?别忘了,我们还有条件没有用: n=n1+n2,n5×104 ,根据均值不等式 n1×n2(a+b2)2 ,可以得到 n1×n2 的上界是 6.25×108 30 秒的情况下是能够跑完的(实际上我用STL自带的链表实现只花了 16 秒)。

代码
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair <ll, ll> p;
ll n, t, x, y, m;
list<p>::iterator it;
list <p> l;

int main() {
    while(scanf("%I64d", &n), n) {
        l.clear();
        while(n--) {
            scanf("%I64d%I64d%I64d", &t, &x, &y);
            p tmp = p(x, y);
            if(t == 1) {
                l.push_front(tmp);
            }
            else if(t == -1) {
                for(it = l.begin(); it != l.end(); ++it) {
                    if(*it == tmp) {
                        l.erase(it);
                        break;
                    }
                }
            }
            else {
                it = l.begin();
                m = (*it).first * x + (*it).second * y;
                for(it = l.begin(); it != l.end(); ++it) {
                    m = max(m, x * (*it).first + y * (*it).second);
                }
                printf("%I64d\n", m);
            }
        }
    }
    return 0;
}

The E-pang Palace(HDU5128)

思路

这题看似枚举两个矩形的8个顶点会超时,但实际上对于每个顶点只要枚举两个顶点就可以了。

代码
#include <bits/stdc++.h>
using namespace std;

typedef pair <int, int> p;
const int maxn = 35, maxy = 205;
bool G[maxy][maxy];
int n, x, y, ans;
p point[maxn];

// 判断是否存在相应的矩形
inline bool isRectangle(int x1, int y1, int x2, int y2) {
    if(x1 == x2 || y1 == y2) {
        return false;
    }
    if(!G[x1][y2] || !G[x2][y1]) {
        return false;
    }   
    return true;
}

// 判断两个矩形是否相交或接触
inline int judge(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
    if(y1 > y2) {
        swap(y1, y2);
    }
    if(y3 > y4) {
        swap(y3, y4);
    }
    if(x1 > x4 || x2 < x3 || y1 > y4 || y2 < y3) {
        return 1;
    }
    if(x1 < x3 && x2 > x4 && y1 < y3 && y2 > y4) {
        return 2;
    }
    if(x3 < x1 && x4 > x2 && y3 < y1 && y4 > y2) {
        return 3;
    }
    return 0;
}

int main() {
    for(; scanf("%d", &n), n;) {
        memset(G, 0, sizeof(G));
        for(int i = 0; i < n; i++) {
            scanf("%d%d", &x, &y);
            point[i] = p(x, y);
            G[x][y] = true;
        }
        if(n < 8) {
            puts("imp");
            continue;
        }
        sort(point, point + n);
        ans = 0;
        // 枚举两个矩形的顶点
        for(int i = 0; i < n; i++) {
            int x1 = point[i].first, y1 = point[i].second;
            for(int j = i + 1; j < n; j++) {
                int x2 = point[j].first, y2 = point[j].second;
                if(!isRectangle(x1, y1, x2, y2)) continue;
                for(int k = 0; k < n; k++) {
                    int x3 = point[k].first, y3 = point[k].second;
                    for(int l = k + 1; l < n; l++) {
                        int x4 = point[l].first, y4 = point[l].second;
                        if(!isRectangle(x3, y3, x4, y4)) continue;
                        int tmp = judge(x1, y1, x2, y2, x3, y3, x4, y4);
                        int area1 = abs(x1 - x2) * abs(y1 - y2);
                        int area2 = abs(x3 - x4) * abs(y3 - y4);                        
                        if(tmp == 1) {
                            ans = max(ans, area1 + area2);
                        }
                        else if(tmp == 2) {
                            ans = area1;
                        }
                        else if(tmp == 3) {
                            ans = area2;
                        }
                    }
                }
            }
        }
        if(ans) {
            printf("%d\n", ans);
        }
        else {
            puts("imp");
        }
    }
    return 0;
}

D.Signal Interference(HDU5130)

思路

首先我们要找到所有满足 |PB|k|PA| 的点 P(x,y) ,根据两点间距离公式展开该不等式有

x2+y2+2(k2xAxB)1k2x+2(k2yA)yB1k2y+x2B+y2Bk2(x2A+y2A)1k20

显然,这个不等式表示的是由圆围成的圆形区域。因此我们可以用圆与多变形交的面积的模板将本题解决。(使用红书模板)

代码
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

const double eps = 1e-8;
const double pi  = acos(-1.0);

inline int cmp(double x) {
    return x < -eps ? -1 : (x > eps);
}

inline double sqr(double x) {
    return x * x;
}

inline double mySqrt(double n) {
    return sqrt(max(0.0, n));
}

// 二维点类
struct Point {
    double x, y;
    Point() {}
    Point(double x, double y): x(x), y(y) {}
    void input() {
        scanf("%lf%lf", &x, &y);
    }
    friend Point operator - (const Point& a, const Point& b) {
        return Point(a.x - b.x, a.y - b.y);
    }
    double norm() {
        return sqrt(sqr(x) + sqr(y));
    }
};

double det(const Point& a, const Point& b) {
    return a.x * b.y - a.y * b.x;
}

double dot(const Point &a, const Point& b) {
    return a.x * b.x + a.y * b.y;
}

const int maxnForPolygon = 510;

// 多边形类
struct Polygon {
    int n;
    Point a[maxnForPolygon];
    Polygon() {}
    void input(int m) {
        n = m;
        for(int i = 0; i < n; i++) {
            a[i].input();
        }
    }
};

// 圆与直线交
void circleCrossLine(Point a, Point b, Point o, double r, Point ret[], int& num) {
    double x0 = o.x, y0 = o.y;
    double x1 = a.x, y1 = a.y;
    double x2 = b.x, y2 = b.y;
    double dx = x2 - x1, dy = y2 - y1;
    double A = sqr(dx) + sqr(dy);
    double B = 2 * dx * (x1 - x0) + 2 * dy * (y1 - y0);
    double C = sqr(x1 - x0) + sqr(y1 - y0) - sqr(r);
    double delta = sqr(B) - 4 * A * C;
    num = 0;
    if(cmp(delta) < 0) {
        return;
    }       
    double t1 = (-B - mySqrt(delta)) / (2 * A);
    double t2 = (-B + mySqrt(delta)) / (2 * A);
    if(cmp(t1 - 1) <= 0 && cmp(t1) >= 0) {
        ret[num++] = Point(x1 + t1 * dx, y1 + t1 * dy);
    }
    if(cmp(t2 - 1) <= 0 && cmp(t2) >= 0) {
        ret[num++] = Point(x1 + t2 * dx, y1 + t2 * dy);
    }
}

// 圆的半径
double Radius;

// 扇形面积
double sectorArea(const Point& a, const Point& b) {
    double theta = atan2(a.y, a.x) - atan2(b.y, b.x);
    while(theta <= 0) {
        theta += 2 * pi;
    }
    while(theta > 2 * pi) {
        theta -= 2 * pi;
    }
    theta = min(theta, 2 * pi - theta);
    return Radius * Radius * theta / 2;
}

double calc(Point& a, Point& b) {
    Point p[2];
    int num = 0;
    int ina = cmp(a.norm() - Radius) < 0;
    int inb = cmp(b.norm() - Radius) < 0;
    if(ina) {
        if(inb) {
            return fabs(det(a, b)) / 2.0;
        }
        else {
            circleCrossLine(a, b, Point(0, 0), Radius, p, num);
            return sectorArea(b, p[0]) + fabs(det(a, p[0])) / 2.0;
        }
    }
    else {
        if(inb) {
            circleCrossLine(a, b, Point(0, 0), Radius, p, num);
            return sectorArea(p[0], a) + fabs(det(p[0], b)) / 2.0;
        }
        else {
            circleCrossLine(a, b, Point(0, 0), Radius, p, num);
            if(num == 2) {
                return sectorArea(a, p[0]) + sectorArea(p[1], b) 
                + fabs(det(p[0], p[1])) / 2.0;
            }
            else {
                return sectorArea(a, b);
            }
        }
    }
}

// 圆与多变形交的面积
double circleCrossPolygon(Polygon& polygon) {
    double ret = 0;
    for(int i = 0; i < polygon.n; i++) {
        int sgn = cmp(det(polygon.a[i], polygon.a[i+1]));
        if(sgn != 0) {
            ret += sgn * calc(polygon.a[i], polygon.a[i+1]);
        }
    }
    return ret;
}

// 根据圆的一半方程求出圆的圆心和半径
int initCircle(double D, double E, double F, Point& O, double& R) {
    R = mySqrt((sqr(D) + sqr(E) - 4 * F) / 4);
    if(R >= 0) {
        O = Point(- D / 2, - E / 2);
        return R > 0;
    }
    return -1;
}

int n, ok, kase = 0;
double k, D, E, F, R;
Point O, A, B;
Polygon polygon;

// 求出圆的一般方程
int initParameter() {
    double d = 1 - sqr(k);
    D = 2 * (sqr(k) * A.x - B.x) / d;
    E = 2 * (sqr(k) * A.y - B.y) / d;
    F = (sqr(B.x) + sqr(B.y) - sqr(k) * (sqr(A.x) + sqr(A.y))) / d;
}

int main() {
    while(scanf("%d", &n) == 1) {
        printf("Case %d: ", ++kase);
        scanf("%lf", &k);
        polygon.input(n);
        A.input();
        B.input();
        initParameter();
        ok = initCircle(D, E, F, O, R);
        if(ok <= 0) {
            printf("%.10f\n", 0.0);
            continue;
        }
        // 模板要求
        Radius = R;
        // 模板要求
        for(int i = 0; i < n; i++) {
            polygon.a[i] = polygon.a[i] - O;
        }
        // 模板要求
        polygon.a[n] = polygon.a[0];
        printf("%.10f\n", fabs(circleCrossPolygon(polygon)));
    }
    return 0;
}

E - Song Jiang’s rank list(HDU5131)

思路

先按照杀人数量和名字对所有好汉排序,然后扫描一遍众好汉,由前面一位好汉的数据计算出当前这位好汉的主排名和次排名。最后输出。

代码

#include <bits/stdc++.h>
using namespace std;

struct outlaw {
    int kill;
    string name;
    bool operator < (const outlaw& o) const {
        if(kill == o.kill) {
            return name < o.name;
        }
        return kill > o.kill;
    }
    inline void input() {
        cin >> name >> kill;
    }
    inline void output() {
        cout << name << ' ' << kill << endl;
    }
};

const int maxn = 210;
int b[maxn], c[maxn];
string q[maxn];
outlaw a[maxn];

int main() {
    ios::sync_with_stdio(false);
    for(int n, m; cin >> n, n;) {
        for(int i = 0; i < n; i++) {
            a[i].input();
        }
        cin >> m;
        for(int i = 0; i < m; i++) {
            cin >> q[i];
        }
        sort(a, a + n);
        for(int i = 0; i < n; i++) {
            a[i].output();
        }
        b[0] = c[0] = 1;
        for(int i = 1; i < n; i++) {
            if(a[i].kill == a[i-1].kill) {
                b[i] = b[i-1];
                c[i] = c[i-1] + 1;
            }
            else {
                b[i] = i + 1;
                c[i] = 1;
            }
        }
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(q[i] == a[j].name) {
                    if(c[j] == 1) {
                        cout << b[j];
                    }
                    else {
                        cout << b[j] << ' ' << c[j];
                    }
                    cout << endl;
                    break;
                }
            }
        }
    }
    return 0;
}

I.Little Zu Chongzhi’s Triangles

思路

木棍的数量相当少,于是理所当然地想到用搜索来解决。按照木棍来搜是行不通的,但是我们可以处理出所有可能的三角形,按照这些三角形来搜。三角形看似很多,但是如果我们在搜到已经被用过木棍的时候及时回溯的话就不会超时了。

代码
#include <bits/stdc++.h>
using namespace std;

const int maxn = 15;
int a[maxn], vis[maxn];
double ans;

struct triangle {
    int i, j, k;
    double area;
    triangle(int i, int j, int k): i(i), j(j), k(k) {
        double p = (a[i] + a[j] + a[k]) / 2.0;
        area = sqrt(p * (p - a[i]) * (p - a[j]) * (p - a[k]));      
    }
};

vector <triangle> v;

void dfs(int cur, double area) {
    if(cur == v.size()) {
        return;
    }
    dfs(cur + 1, area);
    int i = v[cur].i, j = v[cur].j, k = v[cur].k;
    area += v[cur].area;
    if(!vis[i] && !vis[j] && !vis[k]) {
        ans = max(ans, area);
        vis[i] = vis[j] = vis[k] = 1;
        dfs(cur + 1, area);
        vis[i] = vis[j] = vis[k] = 0;
    }
}

int main() {
    for(int n; cin >> n, n;) {
        for(int i = 0; i < n; i++) {
            cin >> a[i];
        }
        sort(a, a + n);
        v.clear();
        for(int i = n - 1; i >= 0; i--) {
            for(int j = i - 1; j >= 0; j--) {
                for(int k = j - 1; k >= 0; k--) {
                    if(a[i] >= a[j] + a[k]) continue;
                    triangle t = triangle(i, j, k);
                    v.push_back(t);
                }
            }
        }
        dfs(0, (ans = 0));
        printf("%.2f\n", ans);
    }
    return 0;
}
思路

后来才知道,这题可以用贪心法解决。将木棍从小到大排序,直接从大到小取。但是我不知道怎么证明贪心策略的正确性。

代码
#include <bits/stdc++.h>
using namespace std;

inline double area(int a, int b, int c) {
    double p = (a + b + c) / 2.0;
    return sqrt(p * (p - a) * (p - b) * (p - c));
}

int main() {
    int n, a[15];
    while(cin >> n && n) {
        for(int i = 0; i < n; i++) {
            cin >> a[i];
        }
        sort(a, a + n);
        double sum = 0;
        for(int i = n - 1; i >= 2;) {
            if(a[i] < a[i-1] + a[i-2]) {
                sum += area(a[i], a[i-1], a[i-2]);
                i -= 3;
            }
            else i--;
        }
        printf("%.2lf\n",sum);
    }
}

K - How Many Maos Does the Guanxi Worth(HDU5137)

思路

因为点的数量比较少,因此可以枚举想要删除的点 i ,每次枚举都求一遍最短路。在求最短路的时候不让i进入队列就相当于在图中把 i 删除了。

代码
#include <bits/stdc++.h>
using namespace std;

typedef pair <int, int> p;
const int maxn = 35, INF = 1e9;
int n, m, ans, whoReject;

struct edge {
    int to, dist;
    edge(int to, int dist): to(to), dist(dist) {}
};

// 最短路模板
struct dijkstra {
    int d[maxn];
    vector <edge> G[maxn];
    void addEdge(int u, int v, int w) {
        G[u].push_back(edge(v, w));
    }
    void init() {
        for(int i = 1; i <= n; i++) {
            G[i].clear();
        }
    }
    int solve(int s, int t) {
        priority_queue < p, vector<p>, greater<p> > pq;
        pq.push(p(0, s));
        fill(d + 1, d + n + 1, INF);
        d[s] = 0;
        while(!pq.empty()) {
            p node = pq.top();
            pq.pop();
            int dist = node.first, u = node.second;
            if(dist > d[u]) continue;
            for(int i = 0; i < G[u].size(); i++) {
                edge& e = G[u][i];
                if(d[e.to] > dist + e.dist) {
                    d[e.to] = dist + e.dist;
                    if(e.to != whoReject) pq.push(p(d[e.to], e.to));
                }
            }
        }
        return d[t];
    }
}o;

int main() {
    for(; scanf("%d%d", &n, &m), n;) {
        o.init();
        for(int u, v, w; m--;) {
            scanf("%d%d%d", &u, &v, &w);
            o.addEdge(u, v, w);
            o.addEdge(v, u, w);
        }
        ans = 0;
        // 枚举要被删除的点
        for(int i = 1; i <= n; i++) {
            whoReject = i;
            ans = max(ans, o.solve(1, n));
        }
        if(ans == INF) puts("Inf");
        else printf("%d\n", ans);
    }
    return 0;
}

(其他题目略)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值