【考试总结】2021.6.26 考试总结

前言

最近考试考得挺多的……这次算,考得还行?\(\text{rk16}\)

话说 Peter 崛起了啊,\(\text{rk7}\)……

然鹅我和他都因为数组开小少得了 \(\text{60pts}\)……

OJ最终成绩:\(40+100+55+5+0=200\)

Lemon 上不知道。

快乐的题解时间

T1

题意简述

\(T\) 组数据,每组数据给定 \(n\) 个数字,记为 \(a_1,a_2,\ldots a_n\)。在这一组数据中选出 \(5\) 个数字使得这 \(5\) 个数字的乘积最大。


果然是不开 long long 见祖宗啊……

还有就是数组开小,后 \(10\) 个点全 \(\text{RE}\) 了……

我 tm 直接 \(\text{AC 100pts → WA+RE 40pts}\)……


法一:贪心

我们知道乘积要最大,一定要让它尽量为正数

那么,首先我们先按照绝对值的大小将数组从大到小排序。然后我们看一看:

  1. 如果前 \(5\) 个数字中有 \(0\),那说明这组数据中不为 \(0\) 的数少于 \(5\) 个。那么结果一定为 \(0\)

  2. 如果前 \(5\) 个数字的乘积为正数,那么这个乘积一定是最优解。证明很简单,相信大家都明白。

那么如果这 \(5\) 个数的乘积是负数呢?

那么首先我们分析最小那个数(即第 \(5\) 个数)是否可以被换成一个和它正负相反的数字。因为我们知道,当一个乘积为负的时候,只需要换一个就可以了。找当然是从前往后找,找到一个就立刻输出,然后 return。因为这已经是最优解了。

继续,如果找不到怎么办?那我们就找与 \(a_4,a_3,a_2,a_1\) 正负相反的数来代替它(此时的 \(a\) 数组已经排过序,还是按照绝对值从大往小找保证找到的是最优解,当然,前 \(4\) 个数字要从小往大找)。

那如果还是找不到呢?那么,这组数据一定是不能凑出正数。那么我们就只能找绝对值最小的 \(5\) 个数(包括 \(0\))来凑乘积了。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,a[100005];
bool f;
bool cmp(int x,int y){return abs(x)>abs(y);}
bool cmp2(int x,int y){return abs(x)<abs(y);}
void s(){
	int h=0;
	bool b[15]={};
	for(int i=1;i<=5;i++){
		b[i]=(a[i]<0);
		h+=(int)b[i];
		if(!a[i]){
			printf("0\n");
			return;
		}
	}
	if(!(h&1)){
		printf("%lld\n",a[1]*a[2]*a[3]*a[4]*a[5]);
		return;
	}
	bool d=!b[5];
	for(int i=6;i<=n;i++){
		if((a[i]<0)==d){
			printf("%lld\n",a[1]*a[2]*a[3]*a[4]*a[i]);
			return;
		}
	}
	for(int i=4;i;i--){
		if(b[i]!=b[5]){
			int y=a[1]*a[2]*a[3]*a[4]*a[5];
			y/=a[i];
			for(int j=6;j<=n;j++){
				if((a[j]<0)!=b[i]){
					y*=a[j];
					printf("%lld\n",y);
					return;
				}
			}
		}
	}
	sort(a+1,a+n+1,cmp2);
	printf("%lld\n",a[1]*a[2]*a[3]*a[4]*a[5]);
	return;
}
signed main(){
	freopen("maximum.in","r",stdin);
	freopen("maximum.out","w",stdout);
	scanf("%lld",&t);
	while(t--){
		f=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%lld",&a[i]),f=f|(!a[i]);
		sort(a+1,a+n+1,cmp);
		s();
	}
	return 0;
}

法二:DP

As we all know,最大的乘积是有可能从最小的数(绝对值比较大)乘一个负数得到~

所以,如果我们使用 DP 来求解这个问题的话,需要开两个 \(dp\) 数组来分别存最大值和最小值。

如果要求最大值(至少得是个正数吧),有两种方法来求:

  1. 两个正数(其中一个是前面的最大值)相乘;

  2. 两个负数(其中一个是前面的最小值)相乘。

求最小值也有两种方法,不过我现在用的这个电脑输入法实在是太不好用了,所以我真的不想写了。。。

见代码……

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,n,a[100005];
ll dp1[100005][6],dp2[100005][6];
signed main(){
	freopen("maximum.in","r",stdin);
	freopen("maximum.out","w",stdout);
	scanf("%d",&t);
	while(t--){
		memset(dp1,128,sizeof(dp1));
		memset(dp2,127,sizeof(dp2));
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=0;i<=n;i++) dp1[i][0]=dp2[i][0]=1;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=min(i,(int)5);j++){
				dp1[i][j]=max(max(dp1[i-1][j-1]*a[i],dp2[i-1][j-1]*a[i]),dp1[i-1][j]);
				dp2[i][j]=min(min(dp1[i-1][j-1]*a[i],dp2[i-1][j-1]*a[i]),dp2[i-1][j]);
			}
		}
		printf("%lld\n",dp1[n][5]);
	}
	return 0;
}

T2

水题,打表即可……

Code

//有亿点长的打表代码 qwq
#include<bits/stdc++.h>
using namespace std;
int I,V,X,L,C,D,M,n;
string sc(int x){
	int qian=x/1000;
	x%=1000;
	int bai=x/100;
	x%=100;
	int shi=x/10;
	x%=10;
	string s;
	//千位
	if(qian==1) s="M";
	else if(qian==2) s="MM";
	else if(qian==3) s="MMM";
	//百位
	if(bai==1) s+="C";
	else if(bai==2) s+="CC";
	else if(bai==3) s+="CCC";
	else if(bai==4) s+="CD";
	else if(bai==5) s+="D";
	else if(bai==6) s+="DC";
	else if(bai==7) s+="DCC";
	else if(bai==8) s+="DCCC";
	else if(bai==9) s+="CM";
	//十位
	if(shi==1) s+="X";
	else if(shi==2) s+="XX";
	else if(shi==3) s+="XXX";
	else if(shi==4) s+="XL";
	else if(shi==5) s+="L";
	else if(shi==6) s+="LX";
	else if(shi==7) s+="LXX";
	else if(shi==8) s+="LXXX";
	else if(shi==9) s+="XC";
	//个位 
	if(x==1) s+="I";
	else if(x==2) s+="II";
	else if(x==3) s+="III";
	else if(x==4) s+="IV";
	else if(x==5) s+="V";
	else if(x==6) s+="VI";
	else if(x==7) s+="VII";
	else if(x==8) s+="VIII";
	else if(x==9) s+="IX";
	return s;
}
int main(){
	freopen("introduction.in","r",stdin);
	freopen("introduction.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		string s=sc(i);
		int j=0;
		while(s[j]){
			if(s[j]=='I') I++;
			else if(s[j]=='V') V++;
			else if(s[j]=='X') X++;
			else if(s[j]=='L') L++;
			else if(s[j]=='C') C++;
			else if(s[j]=='D') D++;
			else if(s[j]=='M') M++;
			j++;
		}
	}
	if(I) printf("I %d\n",I);
	if(V) printf("V %d\n",V);
	if(X) printf("X %d\n",X);
	if(L) printf("L %d\n",L);
	if(C) printf("C %d\n",C);
	if(D) printf("D %d\n",D);
	if(M) printf("M %d\n",M);
	return 0;
} 

T3

和 LIS 问题的普通版求解差不多,只是需要多开一维记录前一个是上升还是下降。

zmq 说也可以用贪心统计折点的数量再 \(+1\),但是我交了之后只有 \(\text{45pts}\),也不知道是我实现错了还是他说错了。

Code

#include<bits/stdc++.h>
using namespace std;
int n,num,dp[10005][2],a[10005];
bool b[10005];//0为上升,1为下降
int main(){
	freopen("sawtooth.in","r",stdin);
	freopen("sawtooth.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	dp[1][0]=dp[1][1]=1;
	for(int i=2;i<=n;i++){
		for(int j=1;j<i;j++){
			if(a[i]<a[j]) dp[i][0]=max(dp[i][0],dp[j][1]+1);
			if(a[i]>a[j]) dp[i][1]=max(dp[i][1],dp[j][0]+1);
		}
		num=max(num,max(dp[i][0],dp[i][1]));
	}
	printf("%d",num);
	return 0;
}

T4

\(dp_{i,j}\) 表示前 \(i\)mjl 马进前 \(j\) 个马棚产生的最小不愉快系数。

对于每一只 mjl 马,我们可以把它和它前面的 一些 mjl 马一起装在一个马棚里,说具体点,就是再用一层循环,\(q\) 代表从第 \(q\)mjl 马开始就和第 \(i\) 只马装在一个马棚里。这样产生的不愉快系数就是 \(dp_{q-1,j-1}\) + 这个马棚里的不愉快系数。那这个不愉快系数怎么算呢?用前缀和统计一下这个区间里黑 mjl 马和白 mjl 马的数量再相乘就可以了。具体看代码。

\(dp\) 初始化成 \(\text{INF}\)\(dp_{0,0}=0\)

Code

#include<bits/stdc++.h>
using namespace std;
int n,k,a[505],b[505],w[505],dp[505][505];
int main(){
	freopen("horse.in","r",stdin);
	freopen("horse.out","w",stdout);
	memset(dp,127,sizeof(dp));
	dp[0][0]=0;
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		w[i]=w[i-1]+(!a[i]);
		b[i]=b[i-1]+a[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=min(i,k);j++){
			for(int q=j;q<=i;q++){
				dp[i][j]=min(dp[i][j],dp[q-1][j-1]+(w[i]-w[q-1])*(b[i]-b[q-1]));
			}
		}
	}
	printf("%d",dp[n][k]);
	return 0;
}

T5

虽然 A 了但是并不明白是怎么回事算了吧……

posted @ 2021-07-12 13:52  Saiodgm  阅读(40)  评论(0编辑  收藏  举报