1 条题解

  • 0
    @ 2025-8-24 23:10:41

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar XGTD
    XGTD 星光铁蛋,不在星光了,但还是星光铁蛋

    搬运于2025-08-24 23:10:40,当前版本为作者最后更新于2025-03-04 08:26:05,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题解:P11839 [USACO25FEB] The Best Lineup S

    Preface

    提供一种 O(n)O(n) 贪心解法,自认为比官解更好理解,代码也非常好写。

    视频题解。

    9 场银组,我终于 Au 了。分数线才 700 分?这应该是 2020 年以来最简单的一场银组了

    Problem Statement

    P11839。 有一个序列,给你一次操作改变原序列顺序的机会,让你从前往后每个数可以选或不选,构造一个字典序最大的序列。

    Solution

    首先,由于是要最大化字典序,显然是从最大的开始,能选就选。

    因此对原数组按照元素大小为第一关键字降序排序,原位置为第二关键字升序排序(至于为什么等会解释)。然后就贪心的从前往后能选就选(因为字典序吗,前面更优就总体更优)。

    如何判断能不能选呢?记录一个 max1max1 和一个 max2max2, 分别代表已经选了的中,原位置最靠后的(max1max1),和已经选了的中原位置第二靠后的(max2max2)。

    此时发现对于一个每一个元素 ii,想选他则他必须要么在所有已经选了的元素之后出现,也就是 posi>max1pos_i > max1或者已经选了的里面只有一个原位置比它靠后,也就是 max1>posi>max2max1 > pos_i > max2。如果这样我们则可以通过用掉我们有的一次操作来把那个那个唯一一个比 ii 靠后的元素挪到 ii 前面。如果连 max2max2 都比 ii 靠后,或者我们已经用了一次操作的机会了,则只能跳过 ii 不选了。

    但是我们只有一次操作,怎么确保把它用到最优的 ii 上呢?其实第一次需要就用就行了。因为假设我们第一次需要的时候不用,而是跳过这个 ii,那不管后面选的有多优,字典序都一定不可能更大了。因为我们是根据元素值的大小排的序。

    但别忘了最后还有两个元素相同的情况,这种情况下我们之所以要以初始位置为第二关键字升序排序就是为了不会在两个相同的元素上因为位置反了而浪费掉操作的机会。

    Code

    实现非常简单,记录 max1max1max2max2,以及是否用过操作机会就行了。

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int T1, n, a[200005];
    pair<int, int> val[200005];
    int cmp(pair<int, int> &x, pair<int, int> &y){
    	if(x.first != y.first)return x.first > y.first;
    	else return x.second < y.second;
    	//if they have different value, return bigger one, otherwise
    	//return the one that appeared earlier
    }
    signed main(){
    	cin>>T1;
    	while(T1--){
    		cin>>n;
    		for(int i = 1; i <= n; i++){
    			cin>>a[i];
    			val[i].first = a[i];
    			val[i].second = i;
    		}
    		int ma = 0, ma2 = 0, moved = 0, outp = 0;
    		sort(val + 1, val + n + 1, cmp);
    		for(int i = 1; i <= n; i++){
    			if(val[i].second > ma){
    				ma2 = ma;				
    				ma = val[i].second;
    				if(outp)cout<<" "<<val[i].first;
    				else cout<<val[i].first;
    				outp = 1;
    			}else if(val[i].second > ma2 && !moved){
    				ma = val[i].second;
    				//we move ma
    				if(outp)cout<<" "<<val[i].first;
    				else cout<<val[i].first;
    				outp = 1;
    				moved = 1;
    //				cout<<val[i].second<<endl;
    			}
    		}
    		cout<<endl;
    	}
    	return 0;
    }
    
    

    赛时代码,略丑。

    After thought

    这场真的有点简单了,我都怀疑 USACO 是不是故意放水,打算再加一个组呀?就像 2015 年加白金之前放水那样。

    求赞。

    • 1

    信息

    ID
    11637
    时间
    2000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者