• Codeforces Round #481 (Div. 3) 解题报告


    A - Remove Duplicates

    题目大意:

    现在有一个长度为n的一串数字,要将其中相同的数字删掉。删除的规则:如果数字相同时保留最右边的那一个。输出删除之后的数字串

    大致思路:

    暴力模拟情况就好···虽然我感觉我写的非常复杂,应该有更简单的写法。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1010;
    int a[55];
    struct Node{
    	int pos,id;
    	Node(){pos=0;id=0;}
    }pos[maxn];
    bool visit[maxn];
    bool cmp(Node a,Node b)
    {
    	return a.pos<b.pos;
    }
    int main()
    {
    	ios::sync_with_stdio(false);
    	memset(visit,false,sizeof(visit));
    	int n,cnt=0;
    	cin>>n;
    	for(int i=1;i<=n;++i){
    		cin>>a[i];
    		if(pos[a[i]].pos==0)
    			cnt++; //数有多少个数字
    		pos[a[i]].pos = i;//记录某一个数出现的最后位置
    		pos[a[i]].id = a[i];
    	}
    	vector<Node> q;
    	cout<<cnt<<endl;
    	for(int i=1;i<=n;++i){//把结果放到数组里就可以了
    		if(visit[a[i]]==false){
    			q.push_back(pos[a[i]]);
    			//cout<<pos[a[i]].pos<<" "<<pos[a[i]].id<<endl;
    			visit[a[i]]=true;
    		}
    	}
    	sort(q.begin(),q.end(),cmp);
    	for(int i=0;i<cnt;++i)
    		cout<<q[i].id<<" ";
    	cout<<endl;
    	return 0;
    }
    

    B - File Name

    题目大意:

    现在有一个长度为n的字符串,现在想让字符串中不存在连续的三个x字符。问需要删除多少个字符。

    大致思路:

    也是暴力模拟就好,循环里一个计数器,看连续的x的个数

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    char str[110];
    int main()
    {
    	int n,ans=0,cnt=0;
    	cin>>n>>str;
    	for(int i=0;i<n;++i){
    		//cout<<i<<" "<<cnt<<" "<<ans<<endl;
    		if(str[i]!='x') cnt=0;
    		else cnt++;
    		if(cnt==3){
    			ans++;
    			cnt--;
    			
    		}
    	}
    	//cout<<str<<endl;
    	cout<<ans<<endl;
    	return 0;
    }
    

    C - Letters

    题目大意:

    题目背景比较复杂,现在简化一下:

    (n) 个房间,按 $ 1...n$ 进行编号, 每个房间里有 (a_i) 个格子。

    (m) 个询问,每个询问给出一个数字,代表着我现在要找的是第 (x) 个格子

    这个 (x) 是对于全局的。比如我现在第一个房间里有10个,第二个房间里有10个。那么第二个房间里的格子的序号就是从 (11...20)

    要求对于每一个询问,输出这个格子所在的房间号和这个格子相对于这个房间的序号。

    解题思路:

    对于 (a_i) 求一个前缀和。对于每一个询问在前缀和里面进行二分,答案就很显然的出来了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+7;
    ll a[maxn],b[maxn],pre[maxn]={0};
    int main()
    {
    	ios::sync_with_stdio(false);
    	int n,m;
    	cin>>n>>m;
    	for(int i=1;i<=n;++i)
    		cin>>a[i];
    	for(int i=1;i<=m;++i)
    		cin>>b[i];
    	for(int i=1;i<=n;++i)
    		pre[i] = a[i]+pre[i-1];
    	for(int i=1;i<=m;++i){
    		int pos = lower_bound(pre+1,pre+1+n,b[i]) -(pre+1);
    		cout<<pos+1<<" "<<b[i] - pre[pos]<<endl;
    	}
    	
    	return 0;
    }
    

    吐槽一下:这个题给了4s,是想让 (O(nm)) 的暴力也能过吗···


    D - Almost Arithmetic Progression

    题目大意:

    有一个长度为 (n) 的数字序列,你可以对每一个数字进行一次 (+1) 或者 (-1) 的操作。

    使得这个序列变成一个等差序列。现在求最小的操作次数,如果不能变成等差序列,就输出 (-1)

    解题思路:

    对于整个序列枚举公差就好了。

    先将数列的最大值和最小值得到,假设为 (maxx)(minl)

    现在设公差为 (d) ,序列长度为 (n)

    那么可以很显然的得到:

    (d imes (n-1) = (maxx-minl-2) ldots (maxx-minl+2))

    然后这个题有一个 (trick) ,就是公差可以是负数,所以就需要扫两次。正向一个,反向一次

    这个算法的最坏情况下就是 (2 imes 4 imes 3 imes n) 次循环,也就是 (2e6)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int INF =1<<30;
    const int maxn=1e5+7;
    int a[maxn];
    int main()
    {
        //freopen("in.txt","r",stdin);
    	ios::sync_with_stdio(false);
    	int n;
    	cin>>n;
    	for(int i=1;i<=n;++i)
            cin>>a[i];
        if(n<=2){
            cout<<0<<endl;
            return 0;
        }
    
    	int maxx = -INF,minl = INF,ans=1<<30;
    	for(int i=1;i<=n;++i)
            maxx = max(maxx,a[i]),minl = min(minl,a[i]);
    
    	//cout<<maxx<<" "<<minl<<endl;
    	int d;
    	bool flag=false;
    	for(int i=maxx-minl-2;i<=maxx-minl+2;++i){ //正向
    		if(i%(n-1)==0){
    			d=i/(n-1); //枚举公差
    			ll now;
    			//cout<<d<<endl;
    			for(int i=-1;i<=1;++i){
    				int cnt=0,x;
    				now = a[1] +i;
    				for(int i=1;i<=n;++i){
    					x=abs(now-a[i]);
    					if(x>1){now=0;break;}
    					if(x!=0) cnt++;
    					now+= d;
    				}
    				if(now){
                        //cout<<d<<" "<<a[1]+i<<endl;
    					ans =min(ans,cnt);
    					flag=true;
    				}
    	 		}
    		}
    	}
    	if(!flag)
    	for(int i=minl-maxx-2;i<=minl-maxx+2;++i){ //反向
    		if(i%(n-1)==0){
    			d=i/(n-1);
    			ll now;
    			//cout<<d<<endl;
    			for(int i=-1;i<=1;++i){
    				int cnt=0,x;
    				now = a[1] +i;
    				for(int i=1;i<=n;++i){
    					x=abs(now-a[i]);
    					if(x>1){now=0;break;}
    					if(x!=0) cnt++;
    					now+= d;
    				}
    				if(now){
                        //cout<<d<<" "<<a[1]+i<<endl;
    					ans =min(ans,cnt);
    					flag=true;
    				}
    	 		}
    		}
    	}
    	if(flag)
    		cout<<ans<<endl;
    	else
    		cout<<-1<<endl;
    	return 0;
    }
    

    E - Bus Video System

    题目大意:

    有一辆公交车,现在知道连续 (n) 个站的上下人数之和,还有车的容量。

    问这个车在开到这n个站之前的人数可能情况有多少种。

    解题思路:

    将情况简单模拟一下,就可以知道有以下的规律:

    在这 (n) 个站的过程中,出现的最大数大于0,说明之前肯定有至少有这么多的空位。

    若出现的最小数<0,则说明之前肯定至少有这么多人已经在车上了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int INF =1<<30;
    int main()
    {
        int n,w,num;
        while(cin>>n>>w)
        {
            int now=0,maxx=-INF,minl=INF;
            for(int i=0;i<n;++i){
                cin>>num;
                now+=num;
                maxx = max(maxx,now);
                minl = min(minl,now);
            }
            int ans = w+1;
            if(minl!=-INF && minl<0)
                ans+=(minl);
            if(maxx!=INF && maxx >0)
                ans-=(maxx);
            if(ans<0)
                ans=0;
            //cout<<maxx<<" "<<minl<<endl;
            cout<<ans<<endl;
        }
        return 0;
    }
    

    F - Mentors

    题目大意:

    (n) 个人,每一个人有一个技能值 (r)(m) 对人之间有争吵。现在定义一个关系:教师

    (forall a,b in n , r_a > r_b)(a)(b) 之间没有争吵,那么 (a) 可以做 (b) 的教师

    现在问对于每一个人,能做多少个人的教师?

    解题思路:

    给每一个人建一个set。

    意义为:(forall x in set[a] , r_a > r_x)

    也就是说 (a) 可以做 (x) 的教师

    然后把每个人的信息按技能值排序。到时候看排序后的位置减去 (set) 的大小就可以了。

    但这里还涉及到一个问题:存在多个人技能值相同 。

    也就是说不能直接按照排序后的位置直接当成答案,还需要去找到第一个比自己技能值小的人的位置。

    最简单暴力的方法就是每一次循环暴力找

    如我第一次提交的代码:

    for(int i=n;i>=1;--i){
            int j=i-1;
            while(pro[i].r<=pro[j].r) //暴力寻找第一个比i小的位置
                j--;
            ans[pro[i].id] = j - st[pro[i].id].size();
        }
    

    但这样会很容易的被数据卡主,比如当所有的人的技能值都一样的时候。直接退化成一个 (O(n^2)) 的循环, (2e^5) 的数据范围肯定会炸。

    改良方法

    利用一个辅助数组,预处理最近一个比他小的位置。光用文字不是很好说,看一下代码然后手动模拟一遍应该就清楚了。

        int cnt=1,pre=1;
        Node now; // Node类型存的是人的信息
        for(int i=1;i<=n;++i){
            if(pro[i].r==now.r)
                cnt++;
            else{
                now = pro[i];
                cnt =1;
            }
            father[i] -=cnt;
        }
    

    然后我们在循环中就可以直接使用这个数组了

        for(int i=n;i>=1;--i)
            ans[pro[i].id] = (i+father[i]) - st[pro[i].id].size();
    

    这样这个循环就是一个稳定的 (O(n))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+7;
    struct Node{
        int r,id;
        Node(){r=id=0;}
    }pro[maxn];
    set<int> st[maxn];
    int ans[maxn]={0},father[maxn]={0};
    bool cmp(Node a,Node b)
    {
        if(a.r==b.r)
            return a.id<b.id;
        return a.r<b.r;
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        ios::sync_with_stdio(false);
        int n,k,x,y;
        cin>>n>>k;
        for(int i=1;i<=n;++i){
            cin>>pro[i].r;
            pro[i].id=i;
        }
        for(int i=1;i<=k;++i){
            cin>>x>>y;
            if(pro[x].r>pro[y].r)
                st[x].insert(y);
            else if(pro[x].r<pro[y].r)
                st[y].insert(x);
        }
        /*for(int i=1;i<=n;++i){
            cout<<i<<" ";
            for(set<int>::iterator it=st[i].begin();it!=st[i].end();++it)
                cout<<*it<<" ";
            cout<<endl;
        }
        cout<<endl;*/
        sort(pro+1,pro+1+n,cmp);
    
        int cnt=1,pre=1;
        Node now;
        for(int i=1;i<=n;++i){
            if(pro[i].r==now.r)
                cnt++;
            else{
                now = pro[i];
                cnt =1;
            }
            father[i] -=cnt;
        }
        /*for(int i=1;i<=n;++i)
            cout<<father[i]<<" ";
        cout<<endl;*/
        for(int i=n;i>=1;--i){
            ans[pro[i].id] = (i+father[i]) - st[pro[i].id].size();
        }
        for(int i=1;i<=n;++i)
            cout<<ans[i]<<" ";
        cout<<endl;
        return 0;
    }
    

    G - Petya's Exams

    题目大意:

    (n) 场考试,一共 (m) 天。每一场考试都有三个参数:(s) 通知有这场考试的那一天, (d) 考试的那一天,(c) 复习所需要的天数

    对于每一场考试,都必须复习足够的天数才能满足不挂科。

    现在让你输出让所有考试都能顺利通过的时间安排,如果解不存在,输出 (-1)

    数据保证每一场考试都不在同一天

    (有SPJ,所以只要输出合法即可)

    解题思路:

    贪心的思想。

    按每场考试结束的时间进行排序,然后从越早结束的考试开始,然后同考试通知的日期开始复习,直到天数足够。这样安排的过程中,如果出现日期冲突则说明解不存在。

    (我是根据样例发现的规律,感觉是没毛病的。但我不会证明。)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=110;
    struct Node{
        int s,d,c,id;
    }exam[maxn];
    int ans[maxn]={0};
    bool cmp(Node a,Node b)
    {
        return a.d<b.d;
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        ios::sync_with_stdio(false);
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=m;++i){
            cin>>exam[i].s>>exam[i].d>>exam[i].c;
            exam[i].id=i;
            ans[exam[i].d] = m+1;
        }
        sort(exam+1,exam+1+m,cmp);
        bool flag=false;
        for(int i=1;i<=m;++i){
            int now = exam[i].s,cnt=0;
            while(cnt<exam[i].c&&now<exam[i].d)
            {
                //cout<<now<<endl;
                if(ans[now]==0){
                    ans[now] =exam[i].id;
                    cnt++;
                }
                now ++ ;
            }
            if(cnt<exam[i].c){
                //cout<<exam[i].id<<endl;
                flag=true;
                break;
            }
        }
        if(flag)
            cout<<-1<<endl;
        else{
            for(int i=1;i<=n;++i)
                cout<<ans[i]<<" ";
        }
        return 0;
    }
    

  • 相关阅读:
    状压DP
    string
    hdu3068
    HDU Stealing Harry Potter's Precious(状压BFS)
    状压BFS
    BFS+打印路径
    poj Meteor Shower
    C语言-无符号数与有符号数不为人知的秘密
    keras_实现cnn_手写数字识别
    python_plot画图参数设置
  • 原文地址:https://www.cnblogs.com/SCaryon/p/9043909.html
Copyright © 2020-2023  润新知