2025牛客寒假算法基础集训营1个人补题题解(C、E、H)

比赛链接:https://ac.nowcoder.com/acm/contest/95323
摸鱼一年,归来仍不会贪心。

H 井然有序之窗

知识点:贪心,优先队列

小红希望你构造一个长度为n的排列,需要满足第i个元素的范围在[l,r]范围内。你能帮帮她吗?

核心想法是从1到n顺序填入,首先将给出的区间按左端点的值从小到大排序,用一个容器维护右端点从小到大的值,确定接下来可以往这个区间内填数后将该区间的右端点加入该容器。
至于为什么是维护右区间大小,因为对于剩下的数而言应该优先去考虑那些即将没有机会的区间,还有更大可能性的区间可以后面再考虑。
优先队列中的右端点需要在填到比这些右端点大的数时及时弹出,这种情况就是不能全部填完的,填完数以后再判断目前答案数组是否是一个排列就可以了,方法应该很多,这里用的是set来删数做判断。

AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int ans[N];
priority_queue<pair<pair<int,int>,int>,vector<pair<pair<int,int>,int>>,greater<pair<pair<int,int>,int>>> q;
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> temp;
set<int> st;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >>n;
    for(int i=1;i<=n;i++)
    {
        int l,r;
        cin >>l>>r;
        q.push({{l,r},i});
    }
    for(int i=1;i<=n;i++)
    {
        while(!q.empty()&&q.top().first.first<=i)
        {
            temp.push({q.top().first.second,q.top().second});
            q.pop();
        }
        while(!temp.empty()&&temp.top().first<i)
            temp.pop();
        if(!temp.empty())
        {
            ans[temp.top().second]=i;
            temp.pop();
        }
    }
    for(int i=1;i<=n;i++)
        st.insert(i);
    for(int i=1;i<=n;i++)
    {
        if(st.find(ans[i])!=st.end())
        {
            st.erase(ans[i]);
        }
    }
    if(st.empty())
    {
        for(int i=1;i<=n;i++)
            cout<<ans[i]<<" ";
    }
    else cout<<-1<<'\n';
    return 0;
}

E 双生双宿之错

知识点:中位数

小红定义一个数组是“双生数组”,当且仅当该数组大小为偶数,数组的元素种类恰好为2种,且这两种元素的出现次数相同。例如 {1,1,4,4,1,4} 是双生数组。
现在小红拿到了一个长度为偶数的数组,她可以进行若干次操作,每次操作将选择一个元素,使其加1或者减1。小红希望你计算将该数组变成双生数组的最小操作次数。

可以抽象成一个问题:给你一个数轴和数轴上的一些数,现在让你取一个数,让数轴上已有的数到这个数的距离之和最小。

如果之前见到过类似的问题就很容易能想到是中位数,但这里不止需要一个中位数,需要两个。

那就可以试着扩展一下,我们要保证整个数组变成两个数且操作次数最少,那就可以按照数的大小排序后前后分成两段,再分别应用这种找中位数的思想。比如1 2 3 4 5 6 我们一定会考虑让前半段的3变成2,而不是让后半段的6变成2,不然不是给自己强行上操作次数的强度吗(

然后再考虑一下两段的中位数相等的情况,直接暴力枚举两边+1或者-1的时候取最小操作次数就行了。

(赛时完全没想到,鉴定为当天熬了夜直接昏头了)

AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
vector<int> b1;
vector<int> b2;
int n;
int check(int x,int y)
{
    int ans=0;
    for(int i=0;i<n/2;i++)
        ans+=abs(b1[i]-x);
    for(int i=0;i<n/2;i++)
        ans+=abs(b2[i]-y);
    return ans;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _;
    cin >>_;
    while(_--)
    {
        cin >>n;
        for(int i=1;i<=n;i++)
            cin >>a[i];
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)
        {
            if(i<=n/2)
                b1.push_back(a[i]);
            else b2.push_back(a[i]);
        }
        sort(b1.begin(),b1.end());
        sort(b2.begin(),b2.end());
        int idx=(n/2+1)/2-1;
        int x,y;
            x=b1[idx];
            y=b2[idx];
        int ans=1e18;
        if(x!=y)
            ans=min(ans,check(x,y));
        if(x+1!=y)
            ans=min(ans,check(x+1,y));
        if(x!=y+1)
            ans=min(ans,check(x,y+1));
        if(x-1!=y)
            ans=min(ans,check(x-1,y));
        if(x!=y-1)
            ans=min(ans,check(x,y-1));
        cout<<ans<<'\n';
        b1.clear();
        b2.clear();
    }
    return 0;
}

C 兢兢业业之移

知识点:暴力

题目要求操作次数不超过\(n^3/2\)次,考虑到一个图中1的数量恒定为\(n^2/4\)个,那么很容易得到每个1的移动次数需要控制在2n次之内。

其实直接暴力就可以了,碰到的第一个1移动到(1,1)的位置,第二个1移动到(1,2)的位置,以此类推。至于怎么移动呢?

对于那些在目标位置右边的数而言,先行移动到目标行,再列移动到目标列。

对于那些在目标位置左边C的数而言,先列移动到目标列,再行移动到目标行。

画图可知这样移动可以保证不会影响到那些已经确定好位置的1。

对于已经移动到的目标位置打一个标记,下次遇到在这个位置上的1就不要动他们了。然后更新一下下一步的目标位置就ok。

就是个暴力题。

真的比我想象的要简单好多,我为什么打到一半就跑了啊亏大发了。

AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=550;
char s[N][N];
int nowj[N];
bool vis[N][N];
vector<pair<pair<int,int>,pair<int,int>>> ans;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _;
    cin >>_;
    while(_--)
    {
        int n;
        cin >>n;
        int nowi=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                cin >>s[i][j];
            }
        }
        for(int i=1;i<=n;i++)
            nowj[i]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(s[i][j]=='1'&&vis[i][j]==0)
                {
                    if(j>=nowj[nowi])
                    {
                        int t=i;
                        while(t<nowi)
                        {
                            ans.push_back({{t,j},{t+1,j}});
                            swap(s[t][j],s[t+1][j]);
                            t++;
                        }
                        while(t>nowi)
                        {
                            ans.push_back({{t,j},{t-1,j}});
                            swap(s[t][j],s[t-1][j]);
                            t--;
                        }
                        int k=j;
                        while(k>nowj[nowi])
                        {
                            ans.push_back({{t,k},{t,k-1}});
                            swap(s[t][k],s[t][k-1]);
                            k--;
                        }
                    }
                    else
                    {
                        int k=j;
                        while(k<nowj[nowi])
                        {
                            ans.push_back({{i,k},{i,k+1}});
                            swap(s[i][k],s[i][k+1]);
                            k++;
                        }
                        int t=i;
                        while(t>nowi)
                        {
                            ans.push_back({{t,k},{t-1,k}});
                            swap(s[t][k],s[t-1][k]);
                            t--;
                        }
                    }
                    vis[nowi][nowj[nowi]]=1;
                    if(nowj[nowi]+1==n/2+1)
                        nowi++;
                    else nowj[nowi]++;
                    j--;
                }
            }
        }
        cout<<ans.size()<<'\n';
        for(int i=0;i<ans.size();i++)
        {
            cout<<ans[i].first.first<<" "<<ans[i].first.second<<" "<<ans[i].second.first<<" "<<ans[i].second.second<<'\n';
        }
        ans.clear();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                vis[i][j]=0;
            }
        }
    }
    return 0;
}
posted @ 2025-01-25 21:57  Ritsuki  阅读(181)  评论(0)    收藏  举报