P1007 独木桥
思路:通过贪心可知要离中点越远的人越无法影响到离中点近的人,只要以中点为分解点把人分成两边后,让离中点最近并且这个人所在的一端最远的人在他的一边走完就是最短时间;同理要时间最长则要离自己所在端最近的人往另一端跑即可【实际上会碰面】(因为题目说两个人碰面后会向反方向跑,则可以等价为两人没反向,而是一直向前跑)其他人只要在最远的人碰面后的区间内走就行。
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e4;
int x[N], L, n;
int main()
{
scanf("%d%d", &L, &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &x[i]);
}
if (n == 0)//特判没人的时候
{
printf("0 0");
return 0;
}
sort(x, x + n);
int mid = (1 + L) / 2;
int min = mid, flag = 0;
for (int i = 0; i < n; i++)
{
if (min >= fabs(x[i] - mid))
{
min = fabs(x[i] - mid);
flag = i;
}
}
if (x[flag] < mid)cout << x[flag];
else cout << 1 + L - x[flag];
cout << ' ';
if ((x[0] - 1) < (L + 1 - x[n - 1]))
{
cout << L + 1 - x[0];
}
else cout << x[n - 1];
return 0;
}
排队接水
思路:要让等待的平均时间最短,只要让取水时间最短的人最先打水,每次都这么处理就可以使局部最优变成全局最优
#include<iostream>
#include<algorithm>
using namespace std;
struct a
{
int c, b;
};
a wat[1010];
bool cmp(a x, a y)
{
return x.b < y.b;
}
int main()
{
int n;
double time = 0;
cin >> n;
for (int i = 1; i <= n; i++)
{
wat[i].c = i;
cin >> wat[i].b;
}
sort(wat + 1, wat + 1 + n, cmp);
for (int i = 1; i <= n; i++)
{
printf("%d ", wat[i].c);
}
cout << endl;
for (int j = n - 1; j >= 1; j--)
{
int i = n - j;
time += wat[i].b * j;
}
printf("%.2lf", time / n);
return 0;
}
凌乱的yyy / 线段覆盖
思路:要想让参加的比赛尽可能多则可以采取按照结束时间由早到晚的顺序进行排序,再依次判断是否存在时间重合,符合就记录,不符合就跳过
#include<iostream>
#include<algorithm>
using namespace std;
typedef struct time//定义一个结构体分别储存开始和结束的时间
{
int begin, end;
}Time;
Time t[1000010];
bool cmp(Time a, Time b)
{
return a.end < b.end;
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> t[i].begin >> t[i].end;
}
if (n == 0)//特判无比赛的时候,虽然后面删掉后发现依旧能ac
{
cout << 0;
return 0;
}
sort(t, t + n, cmp);
int count = 1;//因为第一个最早结束的数一定会选所以直接赋值1
for (int i = 0; i < n - 1; i++)
{
if (t[i + 1].begin >= t[i].end)
{
count++;//符合条件就增加
}
else
t[i + 1] = t[i];//不符合条件时后面的t[i + 1]就没有用了,直接拿t[i]把他覆盖
}
cout << count;
return 0;
}
[NOIP2002 提高组] 均分纸牌
思路:想让其一共用最少的步骤均分卡片,则要让每一次操作都能产生靠近题目要求的影响(也就是每一次操作都能产生一个均值)
操作就是从左到右做,对每个碰到的牌堆都做同样的操作,没有对操作顺序的选择,用一种规则从头到后都能走通
#include<iostream>
#include<algorithm>
using namespace std;
int a[110];
int main()
{
int sum = 0, count = 0;
int n, aver = 0;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
sum += a[i];
}
aver = sum / n;
for (int i = 0; i < n; i++)
{
a[i] -= aver;//题目可以只关注每个值与平均值之间的关系
}
for (int i = 0; i < n; i++)//只是改变了操作的顺序,但并未改变数量,相当于每操作一次就会使一堆数符合条件,而这个操作的顺序被忽略了,如果一开始就符合就直接跳过不处理
{
if (a[i] == 0)continue;
a[i + 1] += a[i];
count++;
}
cout << count;
return 0;
}