本文章主要记录第二周做题的思路。如有错误,请大佬斧正。若大佬有更好的思路,也请指教。
本周涉及贪心算法、排序。
一、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;
}
1185





