pku 3038 Flying Right 解题报告
此题可是让我贡献n多个wa,不写个解题报告,可真对不住自己了;而且我在百度与google还没有找到此题的解题报告(目前来说,我搜索不到)。
题意:
约翰的奶牛们开通了一条飞机航线,专门为奶牛服务。每天早上,她们沿着密歇根湖的西岸,从线路的最北端出发飞到最南端,全程经过N个机场(包括头尾两个)。到了下午,她们又会沿着同样的路线飞回最北端。每天都会有数目不同的K群奶牛要求乘坐飞机,一群牛会在某一个机场等待,并希望飞到另外一个特定的机场。
飞机上只能同时容纳C头奶牛乘客,航班的负责牛希望知道在这一天中她们最多可以满足多少头奶牛的要求。飞机可以只将一群牛中的一部分带到目的地。
约定:1≤N≤10,000,1≤K≤50,000,1≤C≤100。
以sample为例:
输入k=4,n=8,c=3
随后输入k行,每行输入3个数据,分别表示为为s,e,m或者e,s,m(其中s<e,m<c),意思是从s(e)站飞到e(s)站有m头牛要从s(e)站飞到e(s)站。可是飞机最多可以容纳c头牛。那么请问可以飞机在一天内最多可以送多少偷牛从地点到终点。
Sample的解法是这样的:从1站到3站送2头牛,在到达2站的过程中载多一头牛,目的地是8站;到了3站,有2头牛到站,飞机就还可以容纳2头牛;飞到4站,载多1头牛,送到7站;同理到了8站就有1头牛下飞机。同理可知8站到3站可以送2头牛到达目的地。那么计算可得,一天内有8头牛到站。
算法:
此题刚开始我觉得貌似dp,可是画出图的时候就出问题了,线与线之间有重叠。我根本就想不出其最优子结构。于是放弃用dp了。与网友讨论可知,此题好像加入贪心的思想。想想看:其实我们只是讨论送去的情况就ok,返回时做多一次就ok了,但要相反来做。我们就做一次我们可以对每个座位进行贪心,我们根据那群牛最快到达终点,那么这个座位就选择这头牛,此头牛到达目的地后了,再选择一头牛。那么对c个座位扫描一次,就可以将结果求出来了。
关键代码:
void solve(Data process[], int p)
{
nt i, j, k;
qsort(process, p, sizeof(process[0]), cmp);
for (i = 1; i <= c; i++)
{
for (j = 0, k = 0; j < p; j++)
{
if (process[j].start > k && process[j].numbercows > 0)
{
k = process[j].end; //k表示下一头牛的起点站必须大于》k
process[j].numbercows--;//此群牛的数量-1
result++; //结果+1
}
}
}
}
可这是错误的。Wa n多个。
想了很久,不知道错在哪里。在网上搜索一下,发现这是贾由写的一篇论文中有一道题就是讲述此题。马上打印重新分析。再经过与网友的讨论,发现贪心是需要全部座位一起贪心的!而不是每个座位去贪。想想看每个座位来贪心的话,那么有些数据就不可能通过了。不符合实际。例如 c= 3, 1 3 1,2 4 1, 3 5 4。每个座位贪心的话,只能为4,但实际上却可以为5的。那么此种贪心就被抛弃了。于是,我们想到另一个贪心模式:每个站每个站来贪心。在一个站中,有2头牛抢一个位置,那么就看谁的目的地是较近的,也就是说我们选择前往目的地最近的那头牛。可如果在飞机上的牛的目的地比还没有上飞机的牛的目的地还远,那么它就应该驱逐下去,另一头牛上飞机。正如贾由所说,不牛道,但这个算法无疑是最好的。
AC代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 50005
struct Data
{
int start, end, numbercows;
};
Data sent[M], back[M];
int k, n, c, s, b, result;
int cmp(const void *a , const void *b)
{
return (*(Data *)a).end - (*(Data *)b).end;
}
int comp(const void *a , const void *b)
{
return (*(Data *)a).start - (*(Data *)b).start;
}
void init()
{
int i, a, bb, cc;
for (i = 0, s = 0, b = 0; i < k; i++)
{
scanf("%d%d%d", &a, &bb, &cc);
if (a <= bb)
{
sent[s].start = a;
sent[s].end = bb;
sent[s++].numbercows = cc;
}
else
{
back[b].start = bb;
back[b].end = a;
back[b++].numbercows = cc;
}
}
}
Data temp[M], res[M];
void solve(Data process[], int p)
{
int i, j, k, sum, now = 0, process_now = 0;
memset(res, 0, sizeof(res));
memset(temp, 0, sizeof(temp));
qsort(process, p, sizeof(process[0]), comp);
for (i = 1; i <= n; i++)
{
k = 0;
for (j = 0; j < now; j++)
{
//res保存可能是结果的数据,每一次扫描都应该要从头更新
if (res[j].end == i)
{
result += res[j].numbercows;
}
else
{
//不符合,那么存放在临时的temp中,以便在这一站与其他牛重新排序。
temp[k++] = res[j];
}
}
for (j = process_now; j < p; j++)
{
if (process[j].start == i)
{
//符合要求的
temp[k++] = process[j];
process_now++;
}
}
//选择离目的地较近的牛群,并存储到res计算。
qsort(temp, k, sizeof(temp[0]), cmp);
sum = 0, j = 0, now = 0;
while (j < k && sum < c)
{
res[now++] = temp[j++];
sum += temp[j - 1].numbercows;
}
if (sum > c)
{
res[now - 1].numbercows -= sum - c;
}
}
}
int main()
{
while (scanf("%d%d%d", &k, &n, &c) > 0)
{
memset(sent, 0, sizeof(sent));
memset(back, 0, sizeof(back));
init();
result = 0;
solve(sent, s);
solve(back, b);
printf("%d/n", result);
}
return 0;
}