【纪中2020.2.13日】模拟赛题解

目录:

T1:晾衣绳
T2:罗密欧与朱丽叶的约会
T3:粉刷栅栏
T4:马蹄印

主要内容:

T1:晾衣绳

题目描述

奶牛们用N(1<=N<=1000)根绳子架起了晾衣绳,以便晒它们刚洗完的衣服。用它们不能弯曲的拇指,奶牛们彻底搞砸了这项工作。试想一下四根绳子是这样排列的:
在这里插入图片描述
绳子交叉了!这个,当然是无法接受的。

奶牛们想把晾衣绳整理好。它们迟钝的头脑只能处理"交换两根绳"的问题。然而奶牛们的手臂(牛的……也叫手臂)很短,受此限制,它们只能交换相邻两根绳子的端点(在上面或者是下边的固定器上)。以上面的图为例,需要做四次这样的交换才能使绳子变成想下面这样:
在这里插入图片描述
帮助奶牛们整理晾衣绳吧。请你找出最小的交换次数,使得这些晾衣绳能够排列整齐。

你将得到有关晾衣绳位置的描述,用整数来表示当前晾衣绳的顺序。这些晾衣绳被标上了1…N的数字。现在可以告诉你的是,这些晾衣绳在上面和下面各N个连接插槽上的出现顺序。

输入

第1行:一个整数N

第2…N+1行:

每一行有两个隔开的整数(均在1…N范围内)。第一个整数表示在上插槽上连接的绳子的ID,第二个整数表示在下插槽上连接的绳子的ID。第二行的两个整数就分别表示了1号上插槽和1号下插槽上连接的绳子;第三行则分别描述了2号上插槽和下插槽上连接的绳子……以此类推。

输出

第1行:一个整数,指出可以把晾衣绳全都弄直的最小交换次数

样例输入

4
4 1
2 3
1 4
3 2

样例输出

4

分析:

看题目中说,只能交换相邻两个端点,就能想到用冒泡排序做。
记录每条绳子的位置,再冒泡排序,每排一次ans++

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a[1001],b[1001],ans,f[1001];
int main()
{
    freopen("laundry.in","r",stdin);
    freopen("laundry.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
    	scanf("%d%d",&a[i],&b[i]);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(a[i]==b[j])
				f[i]=j;  //记录
	for(int i=1;i<=n-1;i++)
		for(int j=i+1;j<=n;j++)  //冒泡排序
			if(f[i]>f[j])
			{
				swap(f[i],f[j]);
				ans++;  //交换一次ans++
			}
	cout<<ans;
    return 0;
}

T2:罗密欧与朱丽叶的约会

题目描述

     农场主约翰养了两群有世仇的牛,它们被称为蒙塔戈斯家族和卡普鲁特家族。蒙塔戈斯家族的成员,罗密欧,爱上了美丽的朱丽叶,但后者正好是卡普鲁特家族的成员。罗密欧希望与朱丽叶约会,但不希望卡普鲁特家族的其他成员发现(否则会有可怕的事情发生!)

​ 罗密欧和朱丽叶希望在牧场栅栏边一块尽可能大的区域中相见,这样他们可以边散步边聊天。然而,这块区域中不应该有太多的卡普鲁特家族成员,否则,他们俩被发现的机会就会大得多。罗密欧发现在整个牧场栅栏边有P块草坪呈直线排列(1<=P<=1000),在这些草坪上总共有N位卡普鲁特家族的成员在吃草(1<=N<=1000)。每个卡普鲁特家族的成员在一些相邻的草坪上吃草。现在,罗密欧求助于聪明的你,希望你能找出一个最大的范围(指一些相邻的草坪),在这个范围的草坪中,至多有C(1<=C<=1000〉位卡普鲁特家族的成员在吃草。

输入

第一行,包含三个整数:N,P,C

第二至N+1行:每行包括一个整数X(1<=X<=P-1),代表一个卡普鲁特家族成员在栅栏边的第X和X+1块草坪之间吃草,多个卡普鲁特家族成员可以在同一块草坪内一起吃草。

输出

只有一行:一个整数,代表一个最大的草坪范围(指这些草坪的块数),在这个范围内最多只有C个卡普鲁特家族的成员在吃草。

样例输入

2 6 1

2 

3 

(解释:栅栏边的草坪分为六块:1 2 3 4 5和6。有两个卡普鲁特家族成员,一个在第二和第三块草坪上吃草,一个在第三和第四块草坪上吃草)

样例输出

3

(解释:在第四块至至第六块草坪间只有一个卡普鲁特家族的成员在吃草)

分析:

这道题正解是前缀和。因为一头牛占两块草坪,我们要多开一个数组记录多算的牛,求前缀和的时候减掉。后面判断一下是否小于等于c,ans取一个最大值。
前缀和及记录草坪和牛部分:

for(int i=1;i<=n;i++){ 
		scanf("%lld",&y); 
		x[y]++;   //多算的牛
		a[y]++;   //牛
		a[y+1]++;   //牛+1的位置
	} 
	for(int i=1;i<=p;i++){ 
		f[i]=f[i-1]+a[i]-x[i];  //求前缀和再减
	} 

但我们注意到,x[y]++,a[y]++后又一加一减,事实上抵消了。
所以可以化简:

for(int i=1;i<=n;i++){ 
		scanf("%lld",&y); 
		a[y+1]++; //化简后只用牛+1的位置
	} 
	for(int i=1;i<=p;i++){ 
		f[i]=f[i-1]+a[i];  //前缀和
	} 

完整CODE:

#include<iostream> 
#include<cstdio>
#include<cstring>
using namespace std;
long long n,p,c,a[1200],x[1010],f[1010],ans,y;
int main(){ 
	freopen("meet.in","r",stdin); 
	freopen("meet.out","w",stdout);
	memset(x,0,sizeof(x)); 
	memset(a,0,sizeof(a)); 
	cin>>n>>p>>c; 
	for(int i=1;i<=n;i++){ 
		scanf("%lld",&y); 
		a[y+1]++;  //记录牛+1的位置
	} 
	for(int i=1;i<=p;i++){ 
		f[i]=f[i-1]+a[i];  //求前缀和
	} 
	for(long long i=1;i<=p;i++){ 
		for(long long j=1;j<i;j++){ 
			if(f[i]-f[j]<=c){  //判断是否符合条件
				ans=max(ans,i-j); //取最大值
			} 
		} 
	}
	cout<<ans;
	return 0;
}

T3:粉刷栅栏

题目描述

农夫约翰最近正在将他的栅栏粉刷一下(这里所有的栅栏都是在一条直线上的)。他是这样来粉刷的:他从位置0出发,然后执行N条指令,例如,指令可以是“10 L”,表示约翰从当前的位置向左移动10个单位的距离,并且粉刷移动过程中遇到的栅栏,又或者是“15 R”,表示约翰从当前的位置向右移动15个单位的距离,并且粉刷移动过程中遇到的栅栏。

问题描述:

给定所有约翰需要移动的指令,请计算所有栅栏中至少被粉刷两次的栅栏的总长度。约翰最多远离初始位置1000000000个单位的距离。

输入

第一行一个正整数N。

接下来第2行到第N+1行,每行表示每条指令。

输出

只有一行一个整数,表示所有栅栏中至少被粉刷两次的栅栏的总长度。

样例输入

6
2 R
6 L
1 R
8 L
1 R
2 R

样例输出

6

分析:

把每个端点存下来
如果左端点lr=0否则lr=1
然后按位置排序
接下来O(n)扫一遍即可
如果左端点k++,右端点k–
如果k>=2那么就说明这部分被刷了至少2次.
跑得还算吧……
一些东西乘2是因为没有乘2时,最后要ans*2
但只有10分……所以只能在那些地方乘2了。

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
int k,n,x,place;
struct paint{
    int place,node;
}a[200001];
bool cmp(paint x,paint y)
{
    return x.place<y.place;
}
char c;
using namespace std;
int main()
{
	//freopen("paint.in","r",stdin);
	//freopen("paint.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %c",&x,&c);
        a[i*2-1].place=place;
        if(c=='R') place+=x;  //先把距离全部加起来
		else place-=x;
        a[i*2].place=place;
        if(a[i*2-1].place<a[i*2].place)
        a[i*2-1].node=0,a[i*2].node=1;  //记录左端点还是右端点
        else 
        a[i*2-1].node=1,a[i*2].node=0;
	}
    sort(a+1,a+2*n+1,cmp);  //位置排序
    int ans=0;
    for(int i=1;i<=n*2;i++)
    {
        if(k>=2) ans+=(a[i].place-a[i-1].place);  //粉刷了两遍
        if(a[i].node==0) k++;  //左端点
		else k--;  //右端点
    }
    cout<<ans;
    return 0;
}

T4:马蹄印

题目描述

 虽然当奶牛贝里斯找到平衡序列后很高兴了,但是他现在对序列提出了一个更高的要求,就是要求每个序列中必须是先一定数量的左括号然后是与左括号相同数量的右括号。例如:(((()))),就是一个完美的平衡序列。

当贝里斯某天在农场上走的时候,他在地上发现了马蹄印,这个农场是一个N*N的方格,每个小方格中都有一个马蹄印。贝里斯希望从方格的最左上角的地方开始出发,然后每次可以向上或者向下或者向左或者向右移动一步,使得他走过的每个小方格中的马蹄印能够组成一个完美的平衡序列。当然了,贝里斯不能重复经过任何小方格。

问题描述:

请帮助贝里斯在这个N*N的方格中找出长度最长的完美序列的长度。

输入

第一行一个正整数N,表示农场的大小。

接下来N行,每行N个字符,表示N*N的方格上马蹄印的分布情况。

输出

只有一行一个整数,表示最长的完美序列的长度,如果不存在这样的完美序列(例如起始位置就是右括号),则输出0。

样例输入

4 
(()) 
()(( 
(()( 
))))

样例输出

8

分析:

这道题我叫它水题也不为过……
它的数据是:n<=5,这么 的数据暗示 着你要用深搜做。不用想太复杂。深搜多了两个参数:l、r,表示当前走到了左括号还是右括号。递归终止条件是l=r,表示当前是平衡序列ans取最大值就ok。差不多是深搜水题

CODE:

#include<cmath>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
int n,ans,vis[11][11],dx[5]={1,0,-1,0},dy[5]={0,1,0,-1};
char a[11][11];
void dfs(int x,int y,int l,int r)
{
	if(l==r)  //左括号数量=右括号数量
	{
		ans=max(l+r,ans);  //取最大值
		return;
	}
	for(int i=0;i<4;i++)
	{  //深搜模板
		int xx=dx[i]+x;
		int yy=dy[i]+y;
		if(xx<=n&&xx>0&&yy<=n&&yy>0&&!vis[xx][yy])
		{
			vis[xx][yy]=1;
			if(a[xx][yy]=='('&&!r)  //是左括号
			{
				dfs(xx,yy,l+1,r);  //左括号+1
			}
			if(a[xx][yy]==')')  //是右括号
			{
				dfs(xx,yy,l,r+1);  //右括号+1
			}
			vis[xx][yy]=0;
		}
	}
}
int main(){
	freopen("hshoe.in","r",stdin);
	freopen("hshoe.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];
		}
	}
	vis[1][1]=1;
	if(a[1][1]==')')  //特判起点是右括号
	{
		printf("0");  //情况不允许,直接输出0
		return 0;
	}
	dfs(1,1,1,0);  //起点不可能是右括号,所以右括号是0
	printf("%d",ans); 
return 0;
}

发布了48 篇原创文章 · 获赞 34 · 访问量 4796

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/104327989