2020-09 CSP真题

2020-09 CSP真题

1. 检测点查询

#include<bits/stdc++.h>
using namespace std;
int n, xx, yy;
struct AC{
    int x, y, num, dis2;
}note[205];
bool cmp(AC a, AC b){
    if(a.dis2 == b.dis2) return a.num < b.num;
    return a.dis2 < b.dis2;
}
int main(){
    cin >> n >> xx >> yy;
    for(int i = 1; i <= n; i++){
        cin >> note[i].x >> note[i].y;
        note[i].num = i;
        note[i].dis2 = (note[i].x - xx) * (note[i].x - xx) + (note[i].y - yy) * (note[i].y - yy);
    }
    sort(note + 1, note + n + 1, cmp);
    for(int i = 1; i <= 3; i++)
        cout << note[i].num << endl;
    return 0;
}

2. 风险人群筛查

  • 题目链接:风险人群筛查

  • 思路:首先判断一下每个人每个时刻是否在危险区域中,统计每个人连续在高危区域的最长时间即可。

#include<bits/stdc++.h>
using namespace std;
int n, k, t, xl, xr, yd, yu;
int cntj = 0, cntd = 0;
struct AC{
    int flg[1005];
}note[25];
int main(){
    cin >> n >> k >> t >> xl >> yd >> xr >> yu;
    for(int i = 1; i <= n; i++){
        memset(note[i].flg, 0, sizeof(note[i].flg));
        for(int j = 1; j <= t; j++){
            int x, y; 
            cin >> x >> y;
            if(xl <= x && x <= xr && yd <= y && y <= yu) note[i].flg[j] = 1;
        }
    }
    for(int i = 1; i <= n; i++){
        int tmp = 0, maxn = 0;
        for(int j = 1; j <= t; j++){
            if(note[i].flg[j]){
                if(note[i].flg[j - 1]) tmp++;
                else tmp = 1;
            }
            else tmp = 0;
            maxn = max(maxn, tmp);
        }
        if(maxn != 0) cntj++;
        if(maxn >= k) cntd++;
    }
    cout << cntj << endl << cntd <<endl;
    return 0; 
}

3. 点亮数字人生

  • 题目链接:点亮数字人生

  • 知识点:拓扑排序

  • 思路

    1. 首先,先根据所有器件排列顺序,形成拓扑排序,可以用拓扑排序判有向图是否成环(按拓扑序将所有有效节点排入队列,若无环则有效节点一定等于所有节点)。
    2. 接下来,根据拓扑序计算每个器件的值即可。注意由于可能多输入,且不知输入个数,则可用 vector 存所有输入即可。而且拓扑排序的性质决定当前器件前的所有器件一定均已完成计算。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define VI vector<int>
#define PII pair<int, int>
const int N = 5e2 + 5;
const int M = 1e4 + 5;
int cas, input, gatenum;
int s, In[M][N * 5], Output[M][N], outputnum[M], out[N];
struct AC{
    int innum, ru;
    string type;
    vector<PII> in;
    VI edge;
}gate[N];
queue<int> q, order, tmporder;
void init(){  //初始化	
    rep(i, 1, gatenum){
        gate[i].in.clear();
        gate[i].edge.clear();
        gate[i].ru = 0;
    }
    while(q.size()) q.pop();
    while(order.size()) order.pop();
}
bool topsort(){  //拓扑排序判断有向图是否有环
    rep(i, 1, gatenum) if(!gate[i].ru) q.push(i);
    while(q.size()){
        int tmp = q.front(); q.pop();
        order.push(tmp);  //存计算顺序
        for(auto now : gate[tmp].edge){
            gate[now].ru--;
            if(!gate[now].ru) q.push(now);
        }
    }
    return order.size() == gatenum;
}
int calc(int snum, vector<PII> in, string type){
    int ans = in[0].first == 1 ? In[snum][in[0].second] : out[in[0].second];
    if(type == "NOT") return !ans;  //非运算只有一个输入,直接输出!!!
    rep(i, 1, in.size() - 1){
        int now = (in[i].first == 1 ? In[snum][in[i].second] : out[in[i].second]);
        if(type == "AND" || type == "NAND") ans &= now;
        else if(type == "OR" || type == "NOR") ans |= now;
        else if(type == "XOR") ans ^= now;
    }
    if(type == "NAND" || type == "NOR") return !ans;
    else return ans;
}
int main(){
    cin >> cas;
    while(cas--){
        cin >> input >> gatenum;
        init();
        //存图
        rep(i, 1, gatenum){
            cin >> gate[i].type >> gate[i].innum;
            rep(j, 1, gate[i].innum){
                string ss; cin >> ss;
                int tmp = stoi(ss.substr(1));
                if(ss[0] == 'O'){ //建图
                    gate[i].ru++;
                    gate[tmp].edge.push_back(i);
                }
                gate[i].in.push_back({ss[0] == 'I' ? 1 : 0, tmp});  //存所有输入数据
            }
        }
        //存操作
        cin >> s;
        rep(i, 1, s) rep(j, 1, input) scanf("%d", &In[i][j]);
        rep(i, 1, s){
            scanf("%d", &outputnum[i]);
            rep(j, 1, outputnum[i]) scanf("%d", &Output[i][j]);
        }
        //查环
        if(!topsort()){
            printf("LOOP\n");
            continue;
        }
        //模拟计算
        rep(i, 1, s){
            tmporder = order;  //不能更改order,所以需要tmporder代替计算
            memset(out, 0, sizeof(out));
            while(tmporder.size()){
                int top = tmporder.front(); tmporder.pop();
                out[top] = calc(i, gate[top].in, gate[top].type);
            }
            rep(j, 1, outputnum[i]) printf("%d ", out[Output[i][j]]);
            printf("\n");
        }
    }
}

4. 星际旅行

  • 题目链接:星际旅行

  • 知识点:计算几何

  • 思路:在 n n n 维空间中,除距离计算不同,其他都和三维是一样的。发现所有点对只有三种情况

    1. 当两点所连直线不在黑洞内时 incirc = 0,两点最短距离为两点直线距离
    2. 当两点所连直线在黑洞内时 incirc = 1,考虑两点为端点的线段是否经过黑洞(是否为钝角来判断),
      1. 若不经过,两点最短距离仍为两点直线距离
      2. 若经过黑洞,则需要切线到黑洞边缘,再沿黑洞边缘到另一个切点。画图得到两个直角边,和一个圆弧。其中直角边用勾股定理计算,圆弧求得对应角度后,用弧长公式计算。

    最后统计即可。

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e3 + 5;
const db eps = 1e-10;
int n, m;
db r, dis[N][N], ans[N][N];
struct AC{
    db dim[105];
}node[N], O;
db distance(AC a, AC b){
    db ans = 0;
    rep(i, 1, n) ans += (a.dim[i] - b.dim[i]) * (a.dim[i] - b.dim[i]);
    return sqrt(ans);
}
db tangent(int num){
    return sqrt(dis[0][num] * dis[0][num] - r * r);
}
bool incirc(db a, db b, db c){
    db p = (a + b + c) / 2.0;
    db s = sqrt(p * (p - a) * (p - b) * (p - c));
    db h = 2.0 * s / a;
    if(fabs(h - r) < eps) return 0;
    else return h < r;
}
db right_tri(int num){
    return acos(r / dis[0][num]);
}
db Cos(db a, db b, db c){
    return (a * a + b * b - c * c) / (2 * a * b);
}
int main(){
    cin >> n >> m >> r;
    rep(i, 1, n) scanf("%lf", &O.dim[i]);
    rep(i, 1, m) rep(j, 1, n) scanf("%lf", &node[i].dim[j]);
    rep(i, 1, m) dis[0][i] = distance(O, node[i]);
    rep(i, 1, m){
        rep(j, i + 1, m){
            dis[i][j] = distance(node[i], node[j]);
            if(incirc(dis[i][j], dis[0][i], dis[0][j])){
                db anglei = Cos(dis[0][i], dis[i][j], dis[0][j]), anglej = Cos(dis[0][j], dis[i][j], dis[0][i]);
                if(anglei < 0 || anglej < 0) ans[i][j] = ans[j][i] = dis[i][j];  //两点在黑洞同侧
                else{
                    db angle = acos(fmax(fmin(Cos(dis[0][i], dis[0][j], dis[i][j]), 1.0), -1.0));  //防止精度问题造成acos越界(最后10分)
                    angle -= right_tri(i) + right_tri(j);
                    ans[i][j] = ans[j][i] = tangent(i) + tangent(j) + r * angle;
                }
            }
            else ans[i][j] = ans[j][i] = dis[i][j];
        }
    }
    rep(i, 1, m){
        db tmp = 0;
        rep(j, 1, m){
            if(j == i) continue;
            tmp += ans[i][j];
        }
        printf("%.14lf\n", tmp);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值