2020-3-7模拟赛题解

T1

做厌了乘法计算题的贝茜,自创了一种新的乘法运算法则。在这套法则里,
A × B A\times B A×B 等于一个取自 A、一个取自 B 的所有数字对的乘积的和。比方说, 123 × 45 123\times45 123×45 等于
1 × 4 + 1 × 5 + 2 × 4 + 2 × 5 + 3 × 4 + 3 × 5 = 54 1\times4 + 1\times5 + 2\times4 + 2\times5 + 3\times4 + 3\times5 = 54 1×4+1×5+2×4+2×5+3×4+3×5=54。对于 2 个给定的数 A、B
( 1 ≤ A , B ≤ 1 0 9 1\leq A,B\leq 10^9 1A,B109),你的任务是,用新的乘法法则计算 A*B 的值。

我才不告诉你我高级班就做过了呢 q w q qwq qwq。。。

#include 
using namespace std;
int main(){
     
	long long a,b,z=0,n,m;
	cin>>a>>b;
	m=a;
	while(m!=0){
     
		n=b;
		while(n!=0){
     
			z=z+(m%10)*(n%10);
			n=n/10;
		}
		m=m/10;
	}
	cout<<z;
	return 0;
}

纯粹的模拟

T2

给定一个由 a-z 和*组成的字符串,其中*可以被替代成任何 a-z 中的字符。
询问将*替换后字典序最小的回文字符串,无解输出-1。

尺取好啊!

2 2 2 边往中间扫。

#include 
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
     
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>inline void write(T x){
     
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
template<typename T>inline void writen(T x){
     
	write(x);
	puts("");
}
string st;
void work(int l,int r){
     
	if(st[l]=='*'){
     
		if(st[r]=='*')st[l]=st[r]='a';
		else st[l]=st[r];
	}else{
     
		if(st[r]=='*')st[r]=st[l];
		else if(st[l]!=st[r]){
     
				puts("-1");
				exit(0);
			}
	}
}
int main(){
     
	cin>>st;
	int n=st.size();
	st=' '+st;
	int l=1,r=n;
	while(l<=r){
     
		work(l,r);
		l++;r--;
	}
	for(int i=1;i<=n;i++)cout<<st[i];
	return 0;
}

T3

有 6 种不同的硬币,现在已知:

  1. 消耗任意 4 种硬币各一枚可以换一个礼品;
  2. 消耗任意 5 种硬币各一枚可以换两个礼品;
  3. 消耗任意 6 种硬币各一枚可以换四个礼品。

有 n 个人,告诉你每个人每种硬币的数量,问每个人最多可以换得到的礼品个数。

一眼贪心

#include 
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
     
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>inline void write(T x){
     
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
template<typename T>inline void writen(T x){
     
	write(x);
	puts("");
}
ll a[12];
int main(){
     
	int T;
	read(T);
	while(T--){
     
		for(int i=1;i<=6;i++)read(a[i]);
		sort(a+1,a+6+1);
		ll ans=(a[1]-a[0])*4+(a[2]-a[1])*2+(a[3]-a[2])*1;
		writen(ans);
	}
	return 0;
}

T4

贝茜听说了一个骇人听闻的消息:一场流星雨即将袭击整个农场,由于流星
体积过大,它们无法在撞击到地面前燃烧殆尽,届时将会对它撞到的一切东西
造成毁灭性的打击。很自然地,贝茜开始担心自己的安全问题。以 FJ 牧场中最
聪明的奶牛的名誉起誓,她一定要在被流星砸到前,到达一个安全的地方(也就
是说,一块不会被任何流星砸到的土地)。如果将牧场放入一个直角坐标系中,
贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。
根据预报,一共有 M 颗流星(1 <= M <= 50,000)会坠落在农场上,其中第 i 颗
流星会在时刻 T_i (0 <= T_i <= 1,000)砸在坐标为(X_i, Y_i)
(0 <= X_i <= 300;0 <= Y_i <= 300)的格子里。流星的力量会将它所在的格子
,以及周围 4 个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。
贝茜在时刻 0 开始行动,它只能在第一象限中,平行于坐标轴行动,每 1 个时刻中,
,她能移动到相邻的(一般是 4 个)
格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻 t 被
流星撞击或烧焦,那么贝茜只能在 t 之前的时刻在这个格子里出现。
请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。

传统的搜索

应该没什么问题

#include 
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
     
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>inline void write(T x){
     
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
template<typename T>inline void writen(T x){
     
	write(x);
	puts("");
}
int n,a[1010][1010],h[1010][1010],xx;
int dx[4]={
     -1,1,0,0};
int dy[4]={
     0,0,-1,1};
void work(int x,int y,int t){
     
	a[x][y]=min(a[x][y],t);
	for(int i=0;i<4;i++){
     
		int tx=x+dx[i],ty=y+dy[i];
		if(tx>=0&&ty>=0)a[tx][ty]=min(a[tx][ty],t);
	}
}
queue<int>x;
queue<int>y;
queue<int>s;
int main(){
     
	memset(a,0x3f,sizeof(a));xx=a[0][0];
	read(n);
	for(int i=1;i<=n;i++){
     
		int x,y,t;
		read(x),read(y),read(t);
		work(x,y,t);
	}h[0][0]=1;
	x.push(0);
	y.push(0);
	s.push(0);
	while(x.size()){
     
		//cout<
		for(int i=0;i<4;i++){
     
			int tx=x.front()+dx[i],ty=y.front()+dy[i];
			if(tx>=0&&ty>=0&&s.front()+1<a[tx][ty]&&!h[tx][ty]){
     
				h[tx][ty]=1;
				if(a[tx][ty]==xx){
     
					//cout<
					cout<<s.front()+1;
					return 0;
				}
				x.push(tx);
				y.push(ty);
				s.push(s.front()+1);
			}
		}
		x.pop();
		y.pop();
		s.pop();
	}puts("-1");
	return 0;
}

T5

给你一个等边三角形,每条边长都是 x,每次操作可以缩小其中一条边的长度,并且三角形不
能退化成直线,询问最少多少次操作可以把这个三角形变成边长为 y 的等边三角形。

想了好久,终于想了出来。

刚开始没想出来倒着推,突然,我把整页的草稿纸反着看了一下,就有了灵感,真的是一道好题。

#include 
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
     
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>inline void write(T x){
     
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
template<typename T>inline void writen(T x){
     
    write(x);
    puts("");
}
int main(){
     
    int x,a,b,c,s=0;
    read(x);
    read(a);
    b=c=a;
    while(a!=x||b!=x||c!=x){
     
        //cout<
        if(a<=b&&a<=c)a=b+c-1;
        else if(b<=a&&b<=c)b=a+c-1;
            else if(c<=a&&c<=b)c=a+b-1;
        a=min(a,x);
        b=min(b,x);
        c=min(c,x);
        //cout<
        s++;
    }cout<<s;
    return 0;
}

T6

FJ 打算好好修一下农场中某条凹凸不平的土路。按奶牛们的要求,修好后的
路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能
同时出现在修好的路中。
整条路被分成了 N 段,N 个整数 A_1, … , A_N (1 <= N <= 2,000)依次描述
了每一段路的高度(0 <= A_i <= 1,000,000,000)。FJ 希望找到一个恰好含 N 个
元素的不上升或不下降序列 B_1, … , B_N,作为修过的路中每个路段的高度。
由于将每一段路垫高或挖低一个单位的花费相同,修路的总支出可以表示为:
|A_1 - B_1| + |A_2 - B_2| + … + |A_N - B_N|
请你计算一下,FJ 在这项工程上的最小支出是多少。FJ 向你保证,这个支出
不会超过 2^31-1。

重点来了!!!敲黑板!!!

首先,我们需要一个性质。这个性质是最终的所有数都源自于原来的数列。

为什么?首先,我们证明一下。

我们从 3 3 3 个数开始想,假设这 3 3 3 个数是 a , b , c a,b,c a,b,c

  1. a < b & b < c & a < c aa<b&b<c&a<c
  • 这种情况,比方说 7 8 9,之后,我们的方案是 7 8 9 是最优方案之一。
  1. a > b & b > c & a > c a>b \And b>c \And a>c a>b&b>c&a>c
  • 这种情况,比方说 7 6 5,之后,我们的方案是 7 6 5 是最优方案之一。
  1. a < b & b > c & a < c ac \And aa<b&b>c&a<c
  • 这种情况,比方说 1 9 7,之后,我们的方案是 1 7 9 是最优方案之一。
  1. a < b & b > c & a > c ac \And a>c a<b&b>c&a>c
  • 这种情况,比方说 8 9 7,之后,我们的方案是 9 9 7 是最优方案之一。
  1. a > b & b < c & a > c a>b \And bc a>b&b<c&a>c
  • 这种情况,比方说 7 5 9,之后,我们的方案是 5 5 9 是最优方案之一。
  1. a > b & b < c & a < c a>b \And ba>b&b<c&a<c
  • 这种情况,比方说 7 5 6,之后,我们的方案是 7 5 5 是最优方案之一。

之后,我们考虑给数列加一个数 d d d。我们刚才已经证明了, a , b , c a,b,c a,b,c 3个数,任意排列都是最优的。现在,我们来分类讨论。

为了方便,假设都是第 1 1 1 种情况, a < b < c a < b < c a<b<c,最优排列是 a , b , c a,b,c a,b,c

  • d ≤ a d \leq a da

这种情况,显然,对不对。比如 a , b , c a,b,c a,b,c 的最优排列是 3 , 5 , 7 , 9 3,5,7,9 3,5,7,9 d = 2 d=2 d=2。我觉得不用解释了。

  • d ≥ a & d ≤ b d\geq a \And d\leq b da&db

这种情况,显然,对不对。比如 a , b , c a,b,c a,b,c 的最优排列是 3 , 5 , 7 , 9 3,5,7,9 3,5,7,9 d = 4 d=4 d=4。我觉得不用解释了。

  • d ≥ b & d ≤ c d\geq b \And d\leq c db&dc

这种情况,显然,对不对。比如 a , b , c a,b,c a,b,c 的最优排列是 3 , 5 , 7 , 9 3,5,7,9 3,5,7,9 d = 6 d=6 d=6。我觉得不用解释了。

  • d ≥ c d\geq c dc

这种情况,显然,对不对。比如 a , b , c a,b,c a,b,c 的最优排列是 3 , 5 , 7 , 9 3,5,7,9 3,5,7,9 d = 10 d=10 d=10。我觉得不用解释了。

竟然打了 4 4 4 个显然,只不过确实显然。

然后,如果您就可以感性理解一下,我们继续加数,肯定依然是最优。

这样代码就很好写了。

大概这样:

#include 
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
     
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>inline void write(T x){
     
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
template<typename T>inline void writen(T x){
     
    write(x);
    puts("");
}
const int MAXN=2e3+10;
int n,a[MAXN],b[MAXN],ans,f[MAXN][MAXN];
bool cmp1(int x,int y){
     
    return x>y;
}
bool cmp2(int x,int y){
     
    return x<y;
}
int getans(){
     
    for(int i=1;i<=n;i++){
     
        for(int j=1;j<=n;j++){
     
            f[i][j]=INT_MAX;
            for(int k=1;k<=j;k++)
				f[i][j]=min(f[i][j],f[i-1][k]+abs(b[i]-a[j]));
        }
    }
    int ans=INT_MAX;
    for(int i=1;i<=n;i++)ans=min(ans,f[n][i]);
    return ans;
}
int main(){
     
    read(n);
    for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
    sort(a+1,a+n+1,cmp1);
    ans=getans();
    sort(a+1,a+n+1,cmp2);
    ans=min(ans,getans());
    cout<<ans;
    return 0;
}

您会发现这个算法复杂度是 O ( n 3 ) O(n^3) O(n3) 的,所以,还需要优化。我们发现找最小值,会有很多的重复计算

所以,我们可以把这个算法,优化成 O ( n 2 ) O(n^2) O(n2)

#include 
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
     
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>inline void write(T x){
     
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
template<typename T>inline void writen(T x){
     
    write(x);
    puts("");
}
const int MAXN=2e3+10;
int n,a[MAXN],b[MAXN],ans,f[MAXN][MAXN];
bool cmp1(int x,int y){
     
    return x>y;
}
bool cmp2(int x,int y){
     
    return x<y;
}
int getans(){
     
    for(int i=1;i<=n;i++){
     
        int ans=INT_MAX;
        for(int j=1;j<=n;j++){
     
            ans=min(ans,f[i-1][j]);
            f[i][j]=ans+abs(b[i]-a[j]);
        }
    }
    int ans=INT_MAX;
    for(int i=1;i<=n;i++)ans=min(ans,f[n][i]);
    return ans;
}
int main(){
     
    read(n);
    for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
    sort(a+1,a+n+1,cmp1);
    ans=getans();
    sort(a+1,a+n+1,cmp2);
    ans=min(ans,getans());
    cout<<ans;
    return 0;
}

总结

这场比赛,我终于 A K \color{skyblue}{AK} AK 了!

只不过,分数跟我的预想差距很大。倒数 2 2 2 题,可谓是灵感题吧。(一道是我吃饭的时候想到的,一道是我睡觉的时候想到的)

然后很多人都 A A A 了,看来还是我太弱了吧。

你可能感兴趣的:(dp,尺取法,模拟赛,题解,算法,尺取,动态规划)