20240806

赛时得分

题目 A B C D E F G H 总分 排名 比例
满分 1000 1200 1500 1700 2100 2100 2300 2600 14500 174 100%
得分 1000 1200 1167 0 0 - - - 3367 142 81.6%

A. 拆盒子(1000/1000)

唐氏题目。开局过题时间 2min1s,成为全场第七个过掉 A 题的人。

从头开始枚举,如果是左括号就把答案 +1;如果有右括号说明这个盒子已经封死了不用再打开,答案 -1;直到枚举到礼物位直接结束即可。


B. 数组调整(1200/1200)

\(\text{100%}\) 得分做法,看到数字大小都在 \(100\) 以内,应该想到枚举答案区间,也就是枚举每一个长度为 \(17\) 的区间 \([i,i+17]\),把当前数 \(a_j\) 移到区间内的最小花费(也就是离左右端点的更小的那个距离)加到 \(ans\) 中,然后取所有 \(ans\) 的最小值即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1001;
ll n,a[N],ans,l,r,minn=0x3f;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=0;i<=83;i++)
    {
        l=i,r=i+17,ans=0;
        for(int j=1;j<=n;j++)
        {
            if(a[j]<l) ans+=(l-a[j])*(l-a[j]);
            else if(a[j]>r) ans+=(a[j]-r)*(a[j]-r);
        }
        minn=min(ans,minn);
    }
    cout<<minn;
    return 0;
}

C. 密码(1500/1500)

赛时差一步,丢了 \(333\) 分。本来以为要用 hash 做的,结果发现直接 substr 就可以解决了。

其实做这道题之前我一直把 substr 的用法理解错了。对于一个字符串 \(s\)s.substr(pos,len) 是截取 \(s\) 的第 \(pos\) 位起,长度为 \(len\) 的串。至于我理解错的我就不说了,免得再记错。

\(\text{100%}\) 得分做法,对于这种套娃题目,想到递归进行分治。如果当当前串长度是偶数时,显然不符合要求,因为奇 + 偶 = 奇,那我们就 return 掉。接着我们 substr 取出前一半串 \(s_1\)(长度一半向下取整),和另一半 \(s_2\)(长度一半向上取整),把第二段删去第一个字符得到 \(s_3\),删去最后一个字符得到 \(s_4\),分别比较一下 \(s_1=s_3\)\(s_1=s_4\),注意这里是分开比较,因为可能存在 \(s_1\)\(s_3,s_4\) 都相等的情况,赛时就是错在这里。如果相等我们 ans++,然后分治 \(s_2\)

第二轮我们取出取出前一半串 \(s_1\)(长度一半向上取整),和另一半 \(s_2\)(长度一半向下取整),然后后面操作 \(s_1\)\(s_2\) swap 一下即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=101,base=233;
string s;
ll l,ans;
void solve(string s)
{
	ll l=s.length(),l2;
	if(l%2==0) return;
	string s1=s.substr(0,l/2),s2=s.substr(l/2,l-l/2);
	l2=s2.length();
	string s3=s2.substr(0,l2-1),s4=s2.substr(1,l2-1);
	if(s1==s3)
	{
		ans++;
		solve(s2);
	}
    if(s1==s4)
    {
        ans++;
        solve(s2);
    }
	s1=s.substr(0,l/2+1),s2=s.substr(l/2+1,l/2);
	l2=s1.length();
	s3=s1.substr(0,l2-1),s4=s1.substr(1,l2-1);
	if(s2==s3)
	{
		ans++;
		solve(s1);
	}
    if(s2==s4)
    {
        ans++;
        solve(s1);
    }
	return;
} 
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>s;
	solve(s);
	cout<<ans;
	return 0;
}

D. 序列操作(1700/1700)

又是经典的赛时读错题。其实不算读错,是我自己想做法的时候没注意。这个故事告诉我们以后每次策略题要排序的时候想一想题意到底允不允许排序再下手。调程序调不出来的时候一定要自己多手玩几组数,不要不敢手玩,这样才稳。

\(\text{100%}\) 得分做法,直接依题意模拟即可。先差分一下,然后每次循环枚举数组中的每一位,如果当前数和下一个数同号,就扔进一个数组 \(d\) 继续枚举,当异号时把当前位扔进 \(d\) 中,然后我们找到这个同号区间的最小值 \(minn\),把区间内的所有数全部减去 \(minn\),负数就加 \(minn\),继续一直 while(1) 循环直到序列中所有数都为 \(0\) 时结束循环,输出答案即可。

很暴力的做法。但是不知道为啥数据太水了能过,不过这当然也是正解。好像是 dp 更标准才对。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1001;
ll n,a[N],b[N],c[N],d[N],ans,cnt=1;
bool ok=0;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		cin>>b[i];
		c[i]=a[i]-b[i];
	}
	while(1)
    {
        ok=0,cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(c[i]!=0) ok=1;
        }
        if(ok==0) break;
        for(int i=1;i<=n;i++)
        {
            if(c[i]==0 and i<=n) continue;
            if(c[i]*c[i+1]>0) d[++cnt]=c[i];
            else
            {
                d[++cnt]=c[i];
                for(int j=1;j<=cnt;j++) d[j]=abs(d[j]);
                sort(d+1,d+cnt+1);
                ans+=d[1];
                for(int j=i-cnt+1;j<=i;j++)
                {
                    if(c[j]>0) c[j]-=d[1];
                    else c[j]+=d[1];
                }
                cnt=0;
                memset(d,0,sizeof(d));
            }
        }
    }
	cout<<ans;
    return 0;
}

E. 交错序列(0/2100)

这题不唐,出题人才是真的唐。谁家好人出题就放一个数据点?导致这题要么 2100 分,要么 0 分。赛时最后 15min 寻思抢一下这题的暴力分,结果交上去告诉我没有暴力分,破防。

当然 NOIP 要是出这个题肯定是有暴力分的,所以还是写一下暴力题解。

\(\text{0%}\) 得分做法,字典序输出题目,一下子想到 set,那么写这篇题解主要是说一下 next_permutation 枚举全排列的用法。其实这个函数的用法和 sort 很像。我们需要用一个 do-while 循环,里面用 set 或者其他数组等数据结构存一下即可。

那么我们每枚举一个全排列,我们就判断它是否合法,如果合法的话就扔进 set 中,最后直接输出对应第 \(c\) 位的结果即可。

那么注意,next_permutation 的复杂度是 \(\mathcal{O}(n!)\) 的,所以适用的数据范围是,\(n\leq 10\)。那么其实 20240803 的 D 题排列的 \(10pts\) 正确做法就是它。

扔一下本题的暴力代码:

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=21;
ll k,n,c,a[N],cnt;
string s;
set<string> pd;
bool ok=0;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>k;
	while(k--)
	{
		cnt=1;
		cin>>n>>c;
		memset(a,0,sizeof(a));
		pd.clear();
		for(int i=1;i<=n;i++) a[i]=i;
		do
		{
			ok=0;
			s="";
			for(int i=2;i<=n-1;i++)
			{
				if((a[i]>a[i-1] and a[i]>a[i+1]) or (a[i]<a[i-1] and a[i]<a[i+1])) ok=0;
				else
				{
					ok=1;
					break;
				}
			}
			for(int i=1;i<=n;i++) s+=to_string(a[i]);
			if(ok==0) pd.insert(s);
		}while(next_permutation(a+1,a+n+1));
		for(auto i:pd)
		{
			if(cnt==c)
			{
				for(int j=0;j<=i.length();j++) cout<<i[j]<<" ";
				cout<<endl;
				break;
			}
			cnt++;
		}
	}
	return 0;
}
posted @ 2024-08-06 15:01  Lithium_Chestnut  阅读(11)  评论(0)    收藏  举报