IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【板刷 educational round】Educational Codeforces Round 1 A - F -> 正文阅读

[数据结构与算法]【板刷 educational round】Educational Codeforces Round 1 A - F

总结

这场有两个计算几何,被push学习了一下计算几何的基础知识,其它的题都很简单。

A - Tricky Sum

题意

给定正整数 n ( n < = 1 0 9 ) n (n<=10^9) n(n<=109) i i i 1 1 1 n n n ,如果 i i i 为二的整次幂,减去 i i i ,否则加上 i i i

思路

考虑从 1 1 1 加到 n n n ,可以使用等差数列求和公式,再减去其中所有 2 2 2 的整次幂的两倍。复杂度 O ( T ? l o g n ) O(T \cdot logn) O(T?logn)

代码

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

#define int long long

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t; cin >> t;
    while(t--) {
        int x; cin >> x;
        int ans = (1+x)*x/2;
        for(int i = 0;; i++) {
            if((1ll << i ) <= x) ans -= (1ll << i)*2;
            else break;
        }
        cout << ans << endl;
    }

    return 0;
}

B - Queries on a String

题意

给定长度为 1 e 4 1e4 1e4 的字符串, m m m ( m ≤ 300 ) (m \leq 300) (m300) 次操作,每次操作给定 l , r , k l, r, k l,r,k ( k ≤ 1 0 6 ) (k \leq 10^6) (k106),字符串的区间 [ l , r ] [l, r] [l,r] 向右rotate k k k 次,求最终的字符串。

思路

模拟题,但纯模拟一定超时。对于每次操作, O ( 1 ) O(1) O(1) 计算出操作后的起始位置,再进行模拟。复杂度 O ( m ? ∣ s ∣ ) O(m \cdot \vert s \vert) O(m?s)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    string s; cin >> s;
    int m; cin >> m;
    while(m--) {
        int l, r, k; cin >> l >> r >> k;
        l--, r--;
        int len = r - l + 1;
        k %= len;
        if(!k) continue;
        int p = r - k + 1;
        string ns;
        for(int i = p; i<=r; i++) ns.push_back(s[i]);
        for(int i = l; i<=p-1; i++) ns.push_back(s[i]);
        
        for(int i = 0; i<ns.size(); i++) {
            s[i+l] = ns[i];
        }
    }
    cout << s << "\n";
 
    return 0;
}

C - Nearest vectors

题意

给定 n n n 个向量,输出形成的夹角最小的两个向量的编号。

思路

极角排序后扫一遍。复杂度 O ( n ? l o g n ) O(n \cdot logn) O(n?logn)

本题对精度要求非常高,需要使用 long double,并且不能使用余弦定理求两向量的夹角。由于比较之前未进行浮点运算,无精度误差,索性不使用eps,并且用atan2进行极角排序。
如果考虑使用eps,按叉乘排序需要开到1e-8,按atan2排序需要开到1e-16。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
#define double long double
const double pi = acos(-1.);

struct Point {
    int id;
    double x, y;
};
using Vector = Point;
bool operator < (Vector a, Vector b) {
    return atan2(a.y, a.x) < atan2(b.y, b.x);
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n; cin >> n;
    vector<Vector> p(n);
    for(int i = 0; i<n; i++) {
        p[i].id = i+1;
        cin >> p[i].x >> p[i].y;
    }

    sort(p.begin(), p.end());

    double mn = 1e20;
    int ans1, ans2;

    for(int i = 0; i<n-1; i++) {
        Vector a = p[i], b = p[i+1];
        double ang = atan2(b.y, b.x) - atan2(a.y, a.x);
        if(ang < mn) {
            mn = ang; ans1 = p[i].id, ans2 = p[i+1].id;
        }
    }

    Vector a = p[0], b = p[n-1];
    double ang = 2 * pi + atan2(a.y, a.x) - atan2(b.y, b.x);
    if(ang < mn) {
        mn = ang; ans1 = p[0].id, ans2 = p[n-1].id;
    }

    cout << ans1 << " " << ans2 << "\n";

    return 0;
}

D - Igor In the Museum

题意

给定 1000 × 1000 1000 \times 1000 1000×1000 的网格,每个格子为空地或墙壁。对于每一片连通的空地,求这片空地与多少个墙壁四连通。

思路

对于每一单个空地,首先处理出其周围四个方向有对少个墙壁。再通过一次bfs求出每个连通块连接墙壁数量的总和。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int maxn = 1005;
string g[maxn];
int val[maxn][maxn];
bool vis[maxn][maxn];
int r, c, k;
 
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
 
bool valid(int x, int y) {
    return x >= 0 && y >= 0 && x < r && y < c;
}
 
struct Node{
    int x, y;
};
void bfs(int sx, int sy) {
 
    vector<Node> tmp;
 
    queue<Node> q; q.push({sx, sy}); vis[sx][sy] = true;
    int tot = 0;
    while(q.size()) {
        int x = q.front().x, y = q.front().y; 
        tot += val[x][y]; tmp.push_back({x, y});
        q.pop();
        for(int i = 0; i<4; i++) {
            int nx = x + dx[i], ny = y + dy[i];
            if(!valid(nx, ny) || vis[nx][ny] || g[nx][ny] == '*') continue;
            q.push({nx, ny}); vis[nx][ny] = true;
        }
    }
 
    for(auto node : tmp) {
        int x = node.x, y = node.y;
        val[x][y] = tot;
    }
}
 
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> r >> c >> k;
    for(int i = 0; i<r; i++) cin >> g[i];
 
    for(int i = 0; i<r; i++) 
        for(int j = 0; j<c; j++) 
            if(g[i][j] == '.') {
                for(int p = 0; p<4; p++) {
                    int nx = i + dx[p], ny = j+dy[p];
                    if(valid(nx, ny) && g[nx][ny] == '*') val[i][j]++;
                } 
            }
 
 
    for(int i = 0; i<r; i++) for(int j = 0; j<c; j++) {
        if(g[i][j] == '.' && !vis[i][j]) {
            bfs(i, j);
        }
    }
 
    while(k--) {
        int x, y; cin >> x >> y;
        x--, y--;
 
        cout << val[x][y] << "\n";
    }
 
    return 0;
}

E - Chocolate Bar

题意

对于一个由 n n n m m m 列个小格子组成的矩形,我们称之为一块巧克力。对于一块巧克力,我们可以横向或纵向一刀切割为两块子巧克力(小格子是原子性的,无法切割成两部分),每次切割开需要切开长度的平方的代价。给定一块至多 30 × 30 30 \times 30 30×30 的巧克力,我们可以多次切割得到很多子巧克力,从其中选择一些巧克力,使得选择的巧克力总共恰好含有 k k k ( k ≤ 50 ) (k \leq 50) (k50) 个小格子。寻求合法方案的最小代价。

思路

记忆化搜索。dp[i][j][k]表示 i i i j j j 列的巧克力,想得到 k k k 块,需要的最小代价。对于一块巧克力,可以由两个子巧克力横向或纵向拼接得到。也就是说可以枚举当前状态切割能够得到的所有子状态,由子状态转移给当前状态。dp问题复杂度计算方法为状态数量乘转移复杂度,本题有 n × m × k n \times m \times k n×m×k 种状态,转移复杂度为 O ( ( n + m ) ? k ) O((n+m) \cdot k) O((n+m)?k) ,故总复杂度为 O ( n 3 ? k 2 ) O(n^3 \cdot k^2) O(n3?k2)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int dp[33][33][55];
 
int get(int r, int c, int k) {
    if(dp[r][c][k] != -1) return dp[r][c][k];
    if(r*c == k) return dp[r][c][k] = 0;
    if(k == 0) return dp[r][c][k] = 0;
 
    int ret = 0x3f3f3f3f;
 
    for(int t = 1; t<r; t++) {
        int d = r - t;
        for(int a = 0; a<=k; a++) {
            int b = k-a;
            if(t*c >= a && d*c >= b)
                ret = min(ret, get(t, c, a) + get(d, c, b) + c*c);
        }
    }
 
    for(int t = 1; t<c; t++) {
        int d = c - t;
        for(int a = 0; a<=k; a++) {
            int b = k - a;
            if(r*t >= a && r*d >= b)
                ret = min(ret, get(r, t, a) + get(r, d, b) + r*r);
        }
    }
    
    return dp[r][c][k] = ret;
}
 
 
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    memset(dp, -1, sizeof dp);
 
    int n; cin >> n;
    for(int i = 0; i<n; i++) {
        int r,  c, k; cin >> r >> c >> k;
        cout << get(r, c, k) << "\n";
    }
 
    return 0;
}

F - Cut Length

题意

给定一个由 n n n ( n ≤ 1000 ) (n \leq 1000) (n1000) 个点构成的多边形(可凹可凸),给出 m m m ( m ≤ 100 ) (m \leq 100) (m100) 次询问, 每次询问给定一条直线,求直线与多边形公共部分的长度。

思路

对于每次询问,解决方式如下。

首先,结合下面的图片,我们感性地思考一下这个问题。直线 P 1 P 2 → \overrightarrow{{P_1}{P_2}} P1?P2? ? ,可以将整个二维平面分为左右(相对直线而言)两部分。从边 A B → \overrightarrow{AB} AB 开始逆时针遍历多边形的所有边,可以发现所有合法部分都是由多边形从右侧跨越到左侧的边和从左侧跨越到右侧的边夹成的。下图中, B C → \overrightarrow{BC} BC 从右侧跨越到左侧, C D → \overrightarrow{CD} CD 从左侧跨越到右侧,它们之间的部分为总长度做出贡献。同理, D E → \overrightarrow{DE} DE E F → \overrightarrow{EF} EF 之间的部分为总长度做出贡献。同样的, G H → \overrightarrow{GH} GH H I → \overrightarrow{HI} HI I A → \overrightarrow{IA} IA 之间的部分为总长度做出贡献。

接着考虑,“跨越”该如何定义。对于任意一条边,计算其的一个整型值 flag,代表这条边的跨越方式。边和直线所有可能的位置关系如下图所示。

当边的端点在直线两侧时,flag 为 ± 2 \pm 2 ±2 。边的一端在直线上时,flag 为 ± 1 \pm 1 ±1 。边在直线上或与直线不相交,flag为 0 0 0 。对于任意的有向线段 A B → \overrightarrow{AB} AB ,可以通过 s i g n a l ( P 1 P 2 → × P 1 B → ) ? s i g n a l ( P 1 P 2 → × P 1 A → ) signal(\overrightarrow{{P_1}{P_2}} \times \overrightarrow{{P_1}B}) - signal(\overrightarrow{{P_1}{P_2}} \times \overrightarrow{{P_1}A}) signal(P1?P2? ?×P1?B ?)?signal(P1?P2? ?×P1?A ?) 快速计算 flag 值的变化,其中 s i g n a l ( ) signal() signal() 为符号函数,“ × \times ×”代表叉乘。

对于任意一条边,处理出来点 P 1 P_1 P1? 到其的有向距离和 flag 。将所有 flag 不为 0 0 0 的边拿出来,忽略 flag 为 0 0 0 的边,进行下一步操作。

将些边以 P 1 P_1 P1? 到其的有向距离为关键字升序排序,从前至后枚举,过程中维护 flag 的前缀。如果前缀不为 0 0 0 ,说明当前边与下一条边之间的部分为答案作出贡献,将这部分加入到答案中。

代码

#include <bits/stdc++.h>
using namespace std;
 
const int maxn = 1e5;
#define double long double
 
const double eps = 1e-8;
int sign(double x) {
    if(fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}
 
struct Point{
    double x, y;
};
typedef Point Vector;
Vector operator - (Vector a, Vector b) {return Vector{a.x - b.x, a.y - b.y};}
bool operator < (const Point &a, const Point &b) {return a.x < b.x || (a.x == b.x && a.y < b.y);}
double cross(Vector a, Vector b) {return a.x * b.y - a.y * b.x;}
 
double get_length(Point p, Vector v, Point q, Vector w) {
    Vector u = p - q;
    double t = cross(w, u) / cross(v, w);
    return t;
}
 
int n, m;
Point p[maxn];
double solve(Point p1, Point p2) {
    Vector v = p2 - p1;
    vector<pair<double, int> > ans;
    for(int i = 0; i < n; i ++) {
        int f1 = sign(cross(v, p[i] - p1));
        int f2 = sign(cross(v, p[(i + 1) % n] - p1));
        if(f1 == f2) continue;
        double w = get_length(p1, v, p[i], p[(i + 1) % n] - p[i]);
        ans.push_back({w, f1 - f2});
    }
    sort(ans.begin(), ans.end());
    double sum = 0; int flag = 0;
    for(int i = 0; i < ans.size(); i ++) {
        flag += ans[i].second;
        if(flag) sum += ans[i + 1].first - ans[i].first;
    }
    return sum * sqrt(v.x * v.x + v.y * v.y);
}
 
signed main() {
 
    cin >> n >> m;
    for(int i = 0; i<n; i++) cin >> p[i].x >> p[i].y;
    while(m--) {
        Point p1, p2;
        cin >> p1.x >> p1.y >> p2.x >> p2.y;
        printf("%.20Lf\n", solve(p1, p2));
    }
 
    return 0;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 12:01:52  更:2022-04-26 12:05:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:45:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码