POJ 2431 Expedition
坑:未必是按顺序给出输入,要自己先排序!(样例给的是排好序的,但实际上测试数据是乱序的,如果不排序无法确定遇见加油站的先后顺序)
排序时需要借助struct,因为如果只对dis[i]进行排序,无法保持dis与num之间的对应关系。
[关于struct排序中队sort的使用(https://blog.csdn.net/qq_40828914/article/details/80670151)
方法I 贪心 WA→最开始没注意到要排序
采取的贪心策略是:在当前油量范围(可达的所有加油站)内,每次都选择净收益最大的站点。当净收益相同时,选择距离终点最近的哪一个。净收益 = 该站点油量 - 当前位置与该站点的相对距离。
#include <iostream>
#include <cstdio>
using namespace std;
//N [1, 10000] 数轴总长度1000000
//dis num num[1, 100]
//L P
int dis[10010], num[10010];
int main()
{
int N, L, P;
scanf("%d", &N);
for(int i = 1;i <= N;i++)
{
scanf("%d %d", &dis[i], &num[i]); //dis[i]距离目的地的长度 num[i]可获得的油量
//printf("dis[%d] = %d num[%d] = %d\n", i, dis[i], i, num[i]);
}
scanf("%d %d", &L, &P);
if(P - L >= 0)
{
//cout << "无需停靠" << endl;
printf("0\n");
}
else
{
int approach = P, //approach-当前剩余油量
cnt = 0, //记录加油次数
disnow = L, //当前位置距离终点距离
portion = -9999999, //净收益
choosenum = N+1, //净收益最大的选择序号
flag = -1;
while(approach - disnow < 0)//当前剩余油量不足以支撑到达终点 需要继续加油
{
/*净收益版本*/
//cout << "disnow = " << disnow << " approach = " << approach << "portion = " << portion << endl;
portion = -99999999, //存储当前最大的净收益
flag = -1;
for(int i = choosenum-1;i >= 1;i--)
{
if(approach - (disnow - dis[i]) >= 0)//油耗尽前可以抵达该站点进行加油
{
//cout << "approach - (disnow - dis[" << i << "]) = " << approach - (disnow - dis[i]) << endl;
//cout << "approach - (disnow - dis[" << i << "]) >= 0" << endl;
if(num[i] - (disnow - dis[i]) > portion)
{
//cout << "num[" << i << "]-(disnow-dis[" << i << "]) > " << "portion = " << portion << endl;
portion = num[i] - disnow + dis[i];
choosenum = i;
flag = 1;//可以继续下一次加油
//cout << "portion = " << portion << " choosenum = " << choosenum << endl;
}
else if(num[i] - (disnow - dis[i]) == portion)
{
if(dis[i] < dis[choosenum])
choosenum = i;
}
}
}
if(flag > 0)//可以走下去
{
//cout << "flag > 0" << endl;
approach += portion;
disnow = dis[choosenum];
cnt++;
//cout << "approach = "<<approach << " disnow = "<<disnow << " cnt = " <<cnt <<endl;
}
else
{
cnt = -1;
//If it is not possible to reach the town, output -1.
break;
}
/*非净收益版本
cout << "disnow = " << disnow << " approach = " << approach << endl;
portion = 0, flag = -1;
for(int i = choosenum-1;i >= 1;i--)
{
if(approach - disnow + dis[i] >= 0)//油耗尽前可以抵达该站点进行加油
{
cout << "approach - (disnow - dis[" << i << "]) = " << approach - (disnow - dis[i]) << endl;
cout << "approach - (disnow - dis[" << i << "]) >= 0" << endl;
if(approach-disnow+dis[i]+num[i] > portion)
{
cout << "approach-disnow+dis[" << i << "]+num[" << i <<"] > portion" << endl;
portion = approach-disnow+dis[i]+num[i];
choosenum = i;
flag = 1;
cout << "portion = " << portion << " choosenum = " << choosenum << endl;
}
}
}
if(flag > 0)
{
cout << "flag > 0" << endl;
approach = portion;
disnow = dis[choosenum];
cnt++;
cout << "approach = "<<approach << " disnow = "<<disnow << " cnt = " <<cnt <<endl;
}
else
{
cnt = -1;
break;
}*/
}
printf("%d\n", cnt);
}
return 0;
}
后来发现要排序以后改进的贪心。
但是还是WA,想了想觉得是不是这里的问题:
//error注释后面,循环的意图是在当前位置可达的加油站中寻找净收益最大的加油站进行加油,有点说不清楚,但是感觉就是这里和用优先队列实现是否有差别,而且这个“净收益”的定义,虽然我个人编的几个数据符合,但是很可能这个贪心的策略有问题……感觉是这里有点不对。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
//N [1, 10000] 数轴总长度1000000
//dis num num[1, 100]
//L P
struct container
{
int dis, num;
} a[10010];
bool cmp(container m, container n)
{
return m.dis < n.dis; //升序排列
}
int main()
{
int N, L, P;
scanf("%d", &N);
for(int i = 1;i <= N;i++)
{
scanf("%d %d", &a[i].dis, &a[i].num); //dis[i]距离目的地的长度 num[i]可获得的油量
//printf("dis[%d] = %d num[%d] = %d\n", i, dis[i], i, num[i]);
}
scanf("%d %d", &L, &P);
sort(a, a+N, cmp);//按照dis从小到大排列
if(P - L >= 0)
{
//cout << "无需停靠" << endl;
printf("0\n");
}
else
{
int approach = P, //approach-当前剩余油量
cnt = 0, //记录加油次数
disnow = L, //当前位置距离终点距离
portion = -9999999, //净收益
choosenum = N+1, //净收益最大的选择序号
flag = -1;
while(approach - disnow < 0)//当前剩余油量不足以支撑到达终点 需要继续加油
{
/*净收益版本*/
//cout << "disnow = " << disnow << " approach = " << approach << "portion = " << portion << endl;
portion = -99999999, //存储当前最大的净收益
flag = -1;
//error
for(int i = choosenum-1;i >= 1;i--)
{
if(approach - (disnow - a[i].dis) >= 0)//油耗尽前可以抵达该站点进行加油
{
//cout << "approach - (disnow - dis[" << i << "]) = " << approach - (disnow - dis[i]) << endl;
//cout << "approach - (disnow - dis[" << i << "]) >= 0" << endl;
if(a[i].num- (disnow - a[i].dis) > portion)
{
//cout << "num[" << i << "]-(disnow-dis[" << i << "]) > " << "portion = " << portion << endl;
portion = a[i].num - disnow + a[i].dis;
choosenum = i;
flag = 1;//可以继续下一次加油
//cout << "portion = " << portion << " choosenum = " << choosenum << endl;
}
else if(a[i].num - (disnow - a[i].dis) == portion)
{
if(a[i].dis < a[choosenum].dis)
choosenum = i;
}
}
}
if(flag > 0)//可以走下去
{
//cout << "flag > 0" << endl;
approach += portion;
disnow = a[choosenum].dis;
cnt++;
//cout << "approach = "<<approach << " disnow = "<<disnow << " cnt = " <<cnt <<endl;
}
else
{
cnt = -1;
//If it is not possible to reach the town, output -1.
break;
}
/*非净收益版本
cout << "disnow = " << disnow << " approach = " << approach << endl;
portion = 0, flag = -1;
for(int i = choosenum-1;i >= 1;i--)
{
if(approach - disnow + dis[i] >= 0)//油耗尽前可以抵达该站点进行加油
{
cout << "approach - (disnow - dis[" << i << "]) = " << approach - (disnow - dis[i]) << endl;
cout << "approach - (disnow - dis[" << i << "]) >= 0" << endl;
if(approach-disnow+dis[i]+num[i] > portion)
{
cout << "approach-disnow+dis[" << i << "]+num[" << i <<"] > portion" << endl;
portion = approach-disnow+dis[i]+num[i];
choosenum = i;
flag = 1;
cout << "portion = " << portion << " choosenum = " << choosenum << endl;
}
}
}
if(flag > 0)
{
cout << "flag > 0" << endl;
approach = portion;
disnow = dis[choosenum];
cnt++;
cout << "approach = "<<approach << " disnow = "<<disnow << " cnt = " <<cnt <<endl;
}
else
{
cnt = -1;
break;
}*/
}
printf("%d\n", cnt);
}
return 0;
}
方法II 优先队列 RUNTIME ERROR
白书思路的转换:“到达加油站i时,就获得了依次在之后的任何时候都可以加Bi单位汽油的权利”.之后需要加油时,就认为是在之前经过的加油站加的油就可以了。
priority_queue < int > que; (头文件< queue >)
优先队列应该是从小到大的堆,.top()应该取出最顶层的元素,也就是最小值。但是c++STL中,取出数值得到的是最大值。
#include <iostream>
#include <queue>
using namespace std;
#define MAX_N 100000
int L, N, P;
int A[MAX_N+10], B[MAX_N+10];
priority_queue <int> que;
int main()
{
//scanf("%d", &N);
cin >> N;
for(int i = 1;i <= N;i++)
cin >> A[i] >> B[i];
//scanf("%d%d", &A[i], &B[i]);
//scanf("%d%d", &L, &P);
cin >> L >> P;
A[0] = 0; //终点
B[0] = 0;
int ans = 0, //加油次数
pos = L, //现在所在位置
tank = P, //剩余油量
flag = 1;
for(int i = N;i >= 0;i--)
{
//cout <<"pos = " << pos << " A[" << i << "]= " << A[i] << endl;
int d = pos - A[i]; //相对距离
//cout << "d = " << d <<endl;
while(tank - d < 0)
{
//cout << "i = " << i << " tank-d<0" << endl;
if(que.empty())
{
//puts("-1");
cout << -1 << endl;
flag = 0;
}
tank += que.top();
//cout << "que.top()" << que.top() << endl;
//cout << "tank = " << tank << endl;
que.pop();
ans++;
//cout << "ans = " << ans << endl;
}
tank -= d;
//cout << "tank = " << tank << endl;
pos = A[i];
que.push(B[i]);
}
if(flag)
cout << ans << endl;
//printf("%d\n", ans);
return 0;
}