ACM第二周

本文章主要记录第二周做题的思路。如有错误,请大佬斧正。若大佬有更好的思路,也请指教。

本周涉及贪心算法、排序

一、P1007 独木桥

题目:战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳 11 个人通过。假如有 22 个人相向而行在桥上相遇,那么他们 22 个人将无法绕过对方,只能有 11 个人回头下桥,让另一个人先通过。但是,可以有多个人同时呆在同一个位置。

思路:两个士兵相遇时立马转身反向移动,不就相当于两个士兵穿过了对方继续前进吗?

代码:

#include<bits/stdc++.h>
using namespace std;
int f[5000];
int mint(int l,int i)
{
	if(f[i]<=l/2) return f[i];
	else return l-f[i]+1;
}
int maxt(int l,int i)
{
	if(f[i]<=l/2) return l-f[i]+1;
	else return f[i];
}
int main()
{
	int l,n,mintime=0,maxtime=0;
	cin>>l>>n;
	for(int i=0;i<n;i++)
		cin>>f[i];
	for(int i=0;i<n;i++)
	{
		mintime=max(mint(l,i),mintime);
		maxtime=max(maxt(l,i),maxtime);
	}
	cout<<mintime<<" "<<maxtime;
}

二、P1223 排队接水

题目:有 n 个人在一个水龙头前排队接水,假如每个人接水的时间为 Ti​,请编程找出这 n 个人排队的一种顺序,使得 n 个人的平均等待时间最小。输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

思路:前面的人接水时间越短,后面的人等的时间越短。某人等待时间是不包括自己接水的时间的。可以用结构体,成员变量记录每人原本的编号与接水时间。或者,搞点抽象的,如果将每个人接水的时间放大十万倍,这时编号将不影响每人按接水时间排队,同时又能保存每人编号。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	long long n;
	cin>>n;
	long long f[n];
	for(long long i=0;i<n;i++)
	{
		cin>>f[i];
		f[i]=f[i]*100000+i+1;
	}
	sort(f,f+n);
	double sum=0;
	for(long long i=0;i<n;i++)
	{
		//cout<<f[i]%100000<<" ";
		printf("%d ",f[i]%100000);
		sum+=f[i]/100000*(n-i-1);	
	}
	//cout<<endl;
	//cout<<fixed<<setprecision(2)<<sum/n<<endl;
	printf("\n%.2lf",sum/n);	
} 

三、P1803 凌乱的yyy / 线段覆盖

题目:现在各大 oj 上有 n 个比赛,每个比赛的开始、结束的时间点是知道的。yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。所以,他想知道他最多能参加几个比赛。由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 22 个及以上的比赛。

思路:首先要选择开始最早的比赛,当这个比赛结束时间与下个比赛开始时间冲突时,优先选择时间短的比赛,比赛时间短才空的出时间参加别的比赛。

代码:

#include<bits/stdc++.h>
using namespace std;
struct line {
	int be,en;
};
bool com(line a,line b)
{
	return a.en<b.en; 
}
int main()
{
	int n;
    cin >> n;
    line f[n];
	for (int i = 0; i < n; i++)
	{
		cin >> f[i].be>>f[i].en;	
	}
	sort(f,f+n,com);
	int answer=1,temen=f[0].en;
	for (int i = 1; i < n; i++)
	{
		if(f[i].be<temen)
		{
			if(f[i].en<temen)
			{
				temen=f[i].en;
			}
		}
		else
		{
			temen=f[i].en;
			answer++;
		}
	}
	cout<<answer;	
} 

四、P1031 [NOIP2002 提高组] 均分纸牌

题目:有 N 堆纸牌,编号分别为 1,2,…,N。每堆上有若干张,但纸牌总数必为N 的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为 11 堆上取的纸牌,只能移到编号为 22 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N−1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如N=4 时,44 堆纸牌数分别为 9,8,17,69,8,17,6。

移动 33 次可达到目的:

  • 从第三堆取 44 张牌放到第四堆,此时每堆纸牌数分别为 9,8,13,109,8,13,10。
  • 从第三堆取 33 张牌放到第二堆,此时每堆纸牌数分别为 9,11,10,109,11,10,10。
  • 从第二堆取 11 张牌放到第一堆,此时每堆纸牌数分别为 10,10,10,1010,10,10,10。

 思路:贪心算法的核心就在于目光短线,管你下一堆有多少,只要我这堆不够,我就从下一堆拿到够,即使下一堆成了负数也没关系;要是我多了,就把多出来的全扔给下一堆,主打一个目光短浅。

代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int n,sum=0,no=0;
	cin>>n;
	int f[n];
	for(int i=0;i<n;i++)
	{
		cin>>f[i];
		sum+=f[i];
	}
	sum/=n;//每堆的目标数 
	int tem;
	for(int i=0;i<n;i++)
	{
		if(f[i]<sum)
		{
			f[i+1]=f[i+1]-sum+f[i];
			no++;
		}
		else if(f[i]>sum)
		{
			f[i+1]+=f[i]-sum;
			no++;
		}
	}
	cout<<no;
}

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值