第27次CCF CSP认证 个人题解

202209-1 如此编码(小模拟)

第一题不需要任何的算法优化,用最直接最普通的思想去做就能满分,因为数据很小:

但要注意提示内容,可以得知关键的部分怎么写:

正确代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    long long m;
    cin >> n >> m;
    vector<long long> a(n), c(n+1), b(n);
    c[0] = 1;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        c[i+1] = c[i] * a[i];
    }
    for (int i = 0; i < n; i++)
    {
        b[i] = m % c[i+1] - m % c[i];
        b[i] /= c[i];
        cout << b[i] << " ";
    }
    return 0;
}

202209-2 何以包邮?(小优化)

这里提示内容很直球,暴力枚举会超时,只有七十分:

看到物品种类-总价格的题目,首先想到背包问题;然而该题中价格与容量是同一变量(预算作为约束条件),而且要求尽可能等于m的总价格而不是尽可能多。

根本的思路是不变的,通过对每个物品的选与不选的判断使得时间复杂度为O(n),只需要改变策略:dp[i][j]的 i 仍然表示考虑前 i 个物品,j 仍然表示当前的容量上限为 j,如果第 i 件物品不导致超载且总重量更大,就把它装入。

最后再从包邮价格 x 的角度考虑,从 x 到可能的价格上界进行遍历,一旦dp[n][j]不为0,即表示有一种方案在考虑前n个物品的前提下,可以达到 j 的容量,由此获得最接近 m 的容量。

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <numeric>

using namespace std;
long long dp[32][300005] = {0};
int item[32];
int main()
{
    int n, x;
    cin >> n >> x;
    for (int i = 0; i < n; i++)
    {
        cin >> item[i];
    }
    int up = accumulate(item, item+n, 0);
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j <= up; j++)
        {
            dp[i+1][j] = max(dp[i][j], dp[i][j-item[i]] + item[i]);
            // j is for capacity
            if (dp[i+1][j] > j)
                dp[i+1][j] = dp[i][j];
        }
    }
    for (int j = x; j <= up; j++)
    {
        if (dp[n][j] == j)
        {
            cout << j;
            break;
        }
    }
    return 0;
}

202209-3 防疫大数据(大模拟)

大模拟的要点是听话,即,此处不需要任何的算法优化和程序设计的“巧思”,甚至不需要理解。按照提示中的形式化定义去写判断函数,按照题目对问题的描述去分割区间并设置算法,敲进去就对了,否则永远会报错,等debug完了早就考完了。

原本的思路:

class User{
public:
    int id;
    short last;
    int been;
    User(int u, int date, int place) : id(u), last(date), been(place){};
    bool operator>(const User &other) const{
        return last > other.last;
    }

};

一开始想要模仿现实中的软件动态管理数据,以优先队列存储一个个用户,根据活跃时间来删除和判断风险,提高查找效率,实际上根本没法做的,也没必要进行优化。

最终的正确做法是面向过程的,以每一次输入操作为中心的,只要老老实实跟着题目复现就完事了:

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

struct msg{
    int date, user, place;
};
vector<msg> Data[1001];
map<int, pair<short, short>> dura;
map<int, bool> sites;
void setDanger(int place, int date)
{
    if (!sites[place])
        dura[place] = {date, date+6};
    else{
        if (date <= dura[place].second +1)
            dura[place].second = date+6;
        else
            dura[place] = {date, date+6};
    }
    sites[place] = true;
}
// 按照原文去复现
bool check(int d1, int u, int p, int d)
{// 用户u在d1天去过p地点,今天(截止日期)是d
    if (sites[p] && d-7 < d1 && d1 <= d && d1 >= dura[p].first && d <= dura[p].second)
        return true;
    return false;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int days;
    cin >> days;
    for (int today = 0; today < days; today++)
    {
        int ri, mi;
        cin >> ri >> mi;
        for (int r = 0; r < ri; r++)
        {
            int place;
            cin >> place;
            setDanger(place, today);
        }
        for (int m = 0; m < mi; m++)
        {
            int d, u, p;
            cin >> d >> u >> p;
            Data[today].push_back({d,u,p});
        }
        vector<int> ans;
        // 存在一个d0 in (d-7, d],在d0天收到了某一条信息
        for (int d0 = max(today-6, 0); d0 <= today; d0++)
        {
            for(auto &it : Data[d0])
            {
                if (check(it.date, it.user, it.place, today))
                    ans.push_back(it.user);
            }
        }
        sort(ans.begin(), ans.end());
        ans.erase(unique(ans.begin(), ans.end()), ans.end());
        cout << today;
        for (auto &it : ans)
            cout << " " << it;
        cout << endl;
    }

    return 0;
}

202209-4 吉祥物投票(大优化)

难点:最多10^9长度的初始为0序列,需要高效的区间访问和更新。

自创的用map优化区间操作的数据结构,先把投票者视作数轴绑定区间赋值关系,再把吉祥物绑定所有与它相关的投票者区间,同时维护一个表存储当前得分榜。

#include <bits/stdc++.h>

using namespace std;
map<int, pair<int, int>> auds;  // <num, <x, anotherSide>>
map<int, map<int, int>> coms;  // <start, end>
map<int, int> board;
int n, m, q;

void op1(int l, int r, int x){
    for (int i = l ; i <= r;)
    {
        int jump = false;
        if (auds[i].first)
        {
            if (auds[i].second < i){
                int L = auds[i].second, y = auds[i].first;
                // 探测到右端点,向左让出
                auds[L]={y, l-1};
                auds[l-1] = {y, L};
                auds.erase(i);
                // 更新商品的查询表
                coms[y][L] = l-1;
                coms[y][l-1] = L;
                coms[y].erase(i);
                board[y] -= (i-l+1);
            }else if (auds[i].second > i){
                // 除非在l点开始,否则只能探测到左端点
                int R = auds[i].second, y = auds[i].first;
                if (R <= r){
                    // 全部覆盖
                    auds.erase(R);
                    auds.erase(i);
                    // 更新查询表
                    coms[y].erase(R);
                    coms[y].erase(i);
                    board[y] -= R - i + 1;
                    jump = R;
                }
                else{
                    // 向右让出
                    auds[r+1] = {y, R};
                    auds[R] = {y, r+1};
                    auds.erase(i);
                    // 更新查询表
                    coms[y][R] = r+1;
                    coms[y][r+1] = R;
                    coms[y].erase(i);
                    board[y] -= r - i + 1;
                    jump = r;
                }
            }else{
                // 单点删除
                int y = auds[i].first;
                auds.erase(i);
                coms[y].erase(i);
                board[y]--;
            }
        }else
        {
            // 未覆盖
            auds.erase(i);
        }
        if (jump)
            i = jump;
        else
            i++;
    }
    // 写入该区间
    auds[l] = {x, r};
    auds[r] = {x, l};
    coms[x][l] = r;
    coms[x][r] = l;
    board[x] += r - l + 1;
}

void op2(int x, int w){
    if (w) 
        board[w] += board[x];
    board.erase(x);
    auto seq = coms[x];
    for (auto &it : seq)
    {
        int l = it.first, r = it.second;
        if (w)
        {
            auds[l].first = w;
            auds[r].first = w;
            coms[w][l] = r;
            coms[w][r] = l;
        }else{
            auds.erase(l);
            auds.erase(r);
        }
    }

    coms.erase(x);
}
void op3(int x, int y){
    auto seqx = coms[x], seqy = coms[y];
    for (auto &ity : seqy){
        int l = ity.first, r = ity.second;
        auds[l].first = auds[r].first = y;
    }
    for (auto &itx : seqx)
    {
        int l = itx.first, r = itx.second;
        auds[l].first = auds[r].first = x;
    }
    coms[y] = seqx;
    coms[x] = seqy;

    int cachex = board[x];
    board[x] = board[y];
    board[y] = cachex;
}
int op4(int w){
    if (w){
        return board[w];
    }
    else{
        int ans = 0;
        for (auto &every : coms)
        {
            int name = every.first;
            ans += board[name];
        }
        return n - ans;
    }

}
int op5(){
    int maxVal = 0, name = -1;
    for (auto &itb : board)
    {
        int nm = itb.first, v = itb.second;
        if (v > maxVal)
        {
            maxVal = v;
            name = nm;
        }
    }
    if (maxVal == 0)
        return 0;
    return name;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> q;
    vector<int> tt;
    for (int i = 0; i < q; i++)
    {
        short op;
        cin >> op;
        switch (op)
        {
        int x, y, w;
        case 1:
            int l, r;
            cin >> l >> r >> x;
            op1(l,r, x);
            break;
        case 2:
            cin >> x >> w;
            op2(x, w);
            break;
        case 3:
            cin >> x >> y;
            op3(x, y);
            break;
        case 4:
            cin >> w;
            cout << op4(w) << endl;
            // tt.push_back(op4(w));
            break;
        case 5:
            cout << op5() << endl;
            // tt.push_back(op5());
            break;
        }
    }

    // cout << endl;
    // for (auto it:tt)
    // {
    //     cout << it << endl;
    // }
    return 0;
}

通过了测试用例,然而由于未知原因提交永远错误,且超时。

满分通过的代码,运用了珂朵莉树,如果用数组模拟仅得20分:

#include<bits/stdc++.h>
using namespace std;
struct node // Chtholly tree 
{
    int l,r;
    mutable int v;//区间值
    node(int l,int r,int v):l(l),r(r),v(v){}
    bool operator <(const node &a) const {return l<a.l;}//重载<比较符,使区间在set中为从小到大的顺序排列
};
struct Node//维护区间最值 
{
    int k,v;//k编号,v投票数 
    bool operator <(const Node& a) const 
	{
        if(v!=a.v)return v>a.v;
        return k<a.k;
    }
};
using SET=set<node>::iterator;
set<node> t;     // Chthollty tree
int id[200005],ans[200005],f[200005];//懒标记,投票数数组,并查集 
map<int,int> inv;     // 建立标记和标签之间双射
set<Node> all;//维护区间最值
int n,m,q,num;
int find(int k)
{
	if(k==f[k])return k;
	else return f[k]=find(f[k]);
} 
SET split(int pos)
{
	SET it=t.lower_bound(node(pos,0,0));
	if(it!=t.end()&&it->l==pos)return it;
	--it;
	int l=it->l,r=it->r,v=it->v;
	t.erase(it);
	t.insert(node(l,pos-1,v));
	return t.insert(node(pos,r,v)).first;
}
void assign(int l,int r,int v)
{
	SET itr=split(r+1),itl=split(l);
	int father;
	for(SET it=itl;it!=itr;it++)
	{
		father=find(it->v);
		all.erase({father,ans[father]});
		ans[father]-=(it->r-it->l+1);
		all.insert({father,ans[father]});
	}
	t.erase(itl,itr);
    father=find(v);
    t.insert(node(l,r,father));
    all.erase({father,ans[father]});
    ans[father]+=(r-l+1);
    all.insert({father,ans[father]});
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	cin>>n>>m>>q;
	num=m;ans[0]=n;
	for(int i=0;i<=2*max(m,q);i++)id[i]=f[i]=i;
	for(int i=1;i<=m;i++)inv[i]=i;
	t.insert(node(1,n,0));all.insert({0,n});
	for(int i=1;i<=q;i++)
	{
		int x,l,r,v,w;
		cin>>x;
		if(x==1)
		{
			cin>>l>>r>>v;
			assign(l,r,id[v]);
		}
		if(x==2)
		{
			cin>>v>>w;
			int fx=id[v],fy=id[w];
			all.erase({fx,ans[fx]});all.erase({fy,ans[fy]});
			ans[fy]+=ans[fx];
			all.insert({fy,ans[fy]});
			int X=find(fx),Y=find(fy);
			f[X]=Y;//x的父亲为y的父亲
			id[v]=++num;inv[num]=v;//新建一个v编号的标记,以便重新出现时使用 
		}
		if(x==3)
		{
			cin>>v>>w;//注意inv交换 
			swap(inv[id[v]],inv[id[w]]);swap(id[v],id[w]);
		}
		if(x==4)
		{
			cin>>v;
			cout<<ans[id[v]]<<endl;
		}
		if(x==5)
		{
			int maxx=0,idans=0;
			for(auto it:all)
			{
				if(it.k&&it.v>maxx){maxx=it.v;idans=inv[it.k];}
				if(it.k&&it.v==maxx&&inv[it.k]<idans)idans=inv[it.k];//找到相同编号更小的 
            	if(maxx>it.v)break;
			}
			cout<<idans<<endl; 
		}
	}
	return 0;
}

总结:小模拟小优化争取二三十分钟内解决,大模拟老老实实去复现题干的语言,剩下时间专攻大优化尽量得分,第五题图一乐就完了。一以贯之的是一定要准确读题,尤其是提示。

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值