关于acm:牛客小白月赛69题解与分析AF蛋挞玩具开题顺序旅游等腰三角形easy等腰三角形hard

92次阅读

共计 4451 个字符,预计需要花费 12 分钟才能阅读完成。

较量传送门:https://ac.nowcoder.com/acm/contest/52441

感觉整体难度有点偏大。

🎈 作者:Eriktse
🎈 简介:19 岁,211 计算机在读,现役 ACM 银牌选手🏆力争以通俗易懂的形式解说算法!❤️欢送关注我,一起交换 C ++/Python 算法。(优质好文继续更新中……)🚀
🎈 集体博客:www.eriktse.com

A- 蛋挞

签到题。

只需比拟 a / ba % b的大小即可。留神开 longlong。

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

signed main()
{int a, b;scanf("%lld %lld", &a, &b);
    if(a / b < a % b)printf("niuniu eats more than others");
    else if(a / b > a % b)printf("niuniu eats less than others");
    else printf("same");
    return 0;
}

B- 玩具

排序贪婪。

因为咱们要将 n 个玩具全副买下,所以咱们 免单的玩具 价格越高越好,咱们将整个数组排升序后从后往前两个两个拿,且 只付更高价格的玩具的钱

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

const int maxn = 1e6 + 9;
int a[maxn];
signed main()
{int n;scanf("%lld", &n);
    for(int i = 1;i <= n; ++ i)scanf("%lld", a + i);
    sort(a + 1,a + 1 + n);
    
    int ans = 0;
    for(int i = n;i >= 1; -- i)
    {ans += a[i];
        i --;
    }
    printf("%lld\n", ans);
    return 0;
}

C- 开题程序

dfs。

题目数量比拟小,咱们能够枚举出所有的开题程序,而后计算出最终分数取大即可,留神剪枝,当工夫超过 t 的时候能够间接完结,此时的分数曾经有效了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 15;
int a[maxn], b[maxn], c[maxn], x[maxn], y[maxn];
int n, t, p;

bitset<maxn> vis;

// 以后正在选第 dep 道题
int dfs(int dep, int ti, int sc)
{if(ti > t)return 0;// 当累计做题工夫曾经超过了 t 阐明比拟曾经完结了
    if(dep == n + 1)return sc;
    
    int res = sc;
    
    for(int i = 1;i <= n; ++ i)
    {if(vis[i])continue;
        // 切了第 i 道题
        ti += x[i];
        vis[i] = true;
        res = max(res, dfs(dep + 1, ti, sc + max(c[i], a[i] - ti * b[i] - y[i] * p))); 
        vis[i] = false;
        ti -= x[i];
    }
    return res;
}

signed main()
{scanf("%lld %lld %lld", &n, &t, &p);
    for(int i = 1;i <= n; ++ i)
        scanf("%lld %lld %lld %lld %lld", a + i, b + i, c + i, x + i, y + i);

    printf("%lld\n", dfs(1, 0, 0));
    return 0;
}

D- 游览

最小生成树(并查集)+ 二分。

首先咱们晓得要使得所有点互联,且边权尽可能小,应该建设一棵最小生成树,而后修复树中所有的边即可。

而后国家帮忙修复边权 <=p 的局部,那么咱们能够想到,当 p 较大时,牛牛的资金必定能够足够修复剩下的,当 p 较小时,牛牛要修的路就比拟多,就修不了。

所以“y= 牛牛是否修复剩下的路 ”是随着p 枯燥的,当 p 大时,y=1,当 p 小时,y=0,咱们要做的就是找到那个交界处,二分即可。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 9;

map<int, int> mp[maxn];

struct Edge
{int x, y, w;};

int pre[maxn];
// 门路压缩的并查集
int root(int x){return pre[x] = (pre[x] == x ? x : root(pre[x]));}

int a[maxn];// a 外面寄存最小生成树的所有边权
int n, m, c, cnt;

bool check(int k)
{
    int res = 0;// 贪婪求最小代价,数组逆序点乘
    for(int i = cnt, j = 0;i >= 1; -- i)
    {if(a[i] <= k)break;//<= k 的局部国家买单不必思考了
        res += (++ j) * a[i];
    }
    return res <= c;
}

signed main()
{scanf("%lld %lld %lld", &n, &m, &c);
    /* 最小生成树,共 3 步 */
    vector<Edge> vec;
    //1. 存边
    for(int i = 1;i <= m; ++ i)
    {int x, y, w;scanf("%lld %lld %lld", &x, &y, &w);
        vec.push_back({x, y, w});// 将边存入 vec 中
    }
    //2. 将边升序
    sort(vec.begin(), vec.end(), [](const Edge &u, const Edge &v)
         {return u.w < v.w;});
    //3. 贪婪建树,并查集判断连通性
    for(int i = 1;i <= n; ++ i)pre[i] = i;// 并查集初始化
    for(auto &i : vec)
    {
        int x = i.x, y = i.y, w = i.w;
        if(root(x) == root(y))continue;
        a[++ cnt] = w;// a 天然是升序的
        pre[root(x)] = root(y);
    }
    
    /* 生成树完结 */
    
    // 以下为二分局部
    int l = -1, r = 2e9;
    
    while(l + 1 != r)
    {int mid = (l + r) >> 1;
        if(check(mid))r = mid;
        else l = mid;
    }
    printf("%lld\n", r);
    return 0;
}

E- 等腰三角形(easy)

暴力枚举。

枚举出所有三个点组成的三元组,留神不要反复。

能够通过海伦公式来求面积判断是否共线。

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

const int maxn = 500;
const double eps = 1e-6;

struct Point
{int x, y;}p[maxn];

int dist(const Point &u, const Point &v)
{
    int dx = u.x - v.x;
    int dy = u.y - v.y;
    return dx * dx + dy * dy;
}

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

signed main()
{int n;scanf("%lld", &n);
    for(int i = 1;i <= n; ++ i)
        scanf("%lld %lld", &p[i].x, &p[i].y);
    int ans = 0;
    for(int i = 1;i <= n; ++ i)
    {for(int j = i + 1;j <= n; ++ j)
        {for(int k = j + 1;k <= n; ++ k)
            {int d1 = dist(p[i], p[j]);
                int d2 = dist(p[i], p[k]);
                int d3 = dist(p[j], p[k]);
                if(area(sqrt(d1), sqrt(d2), sqrt(d3)) <= eps)continue;
                if(d1 == d2 || d1 == d3 || d2 == d3)ans ++;
            }
        }
    }
    printf("%lld\n", ans);
    return 0;
}

F- 等腰三角形(hard)

这题必定不能暴力枚举了。

咱们能够发现,以整数点作为定点必定无奈形成等边三角形。

如果咱们要形成一个等边三角形,那么就须要有 60 度的角,如果这个 60 度的角由两个角 x,y 相加而成,就有:

$$ tan60\degree = tan(x+y)= \frac{tanx + tany}{1-tanx \times tany}$$

其中 tan60 是一个无理数,然而前面的 tanx, tany 都是有理数,一个无理数无奈通过有理数的加减乘除算出,所以在整数点中结构不出 60 度的角。

咱们枚举每一个点 A,而后枚举其余点作为B,而后再查一下有多少C 即可(也就是和 A 间隔等于 dist(AB) 的点),这里只需保障 C 的下标小于 B 的下标,就保障了一个偏序关系,就不会反复计算。

接下来须要将“三点共线”的这样“非凡等腰三角形”减去,咱们只需计算有多少这样的“线段”即可。

枚举每一个点 A,再查一下A' 是否存在即可,能够对点做一个桶来判断,因为地图并不大。

#include <bits/stdc++.h>
#include <bits/extc++.h>
#define int long long
using namespace std;

const int maxn = 3009, T = 1000;
const double eps = 1e-6;

struct Point
{int x, y;}p[maxn];

int dist(const Point &u, const Point &v)
{
    int dx = u.x - v.x;
    int dy = u.y - v.y;
    return dx * dx + dy * dy;
}

int cnt[2123456];
bitset<2005> vis[2005];

signed main()
{int n;scanf("%lld", &n);
    for(int i = 1;i <= n; ++ i)
    {scanf("%lld %lld", &p[i].x, &p[i].y);
        vis[p[i].x + T][p[i].y + T] = true;
    }
    
    
    int ans = 0;
    for(int i = 1;i <= n; ++ i)
    {for(int j = 1;j <= n; ++ j)
        {if(i == j)continue;
            
            ans += (cnt[dist(p[i], p[j])] ++);
        }
        for(int j = 1;j <= n; ++ j)
        {if(i == j)continue;
            
            cnt[dist(p[i], p[j])] = 0;
        }
    }
    int cnt = 0;
    for(int i = 1;i <= n; ++ i)
    {for(int j = 1;j <= n; ++ j)
        {if(i == j)continue;
            int tx = 2 * p[j].x - p[i].x;
            int ty = 2 * p[j].y - p[i].y;
            if(tx < -500 || tx > 500 || ty < -500 || ty > 500)continue;
            
            if(vis[tx + T][ty + T])cnt ++;
        }
    }
    printf("%lld\n", ans - cnt / 2);
    return 0;
}

🎈 本文由 eriktse 原创,创作不易,如果对您有帮忙,欢送小伙伴们点赞👍、珍藏⭐、留言💬

正文完
 0