题目:推销员
1. 60分解法 枚举+模拟
提出一个命题:
如果
X
=
1
X=1
X=1, 走过的结点集合为
{
i
1
}
\{i_1\}
{i1},
如果
X
=
2
X=2
X=2, 走过的结点集合为
{
i
1
,
i
2
}
\{i_1, i_2\}
{i1,i2}
就是说:
X
=
2
X=2
X=2的集合必然包含
X
=
1
X=1
X=1的集合
证明:
v[i] : i结点的疲劳值
dis[i]: i结点到起点的距离
当x=1
时 maxNode
为最大的结点, 即是V[maxNode] + dis[maxNode]*2 > v[i] + dis[j](i=j=1,2,3...)
当x=2
时 假设最大的疲劳值是q = v[k1] + v[k2] + dis[k2] * 2
其中dis[k2]>dis[k1]
k1!=maxNode, k2!=maxNode
显然可以看出存在 v[k1] +V[maxNode] + dis[maxNode]*2 > q
所
以
假
设
不
成
立
,
k
1
或
k
2
中
必
然
有
一
个
等
于
m
a
x
N
o
d
e
.
所
以
命
题
成
立
所以假设不成立,k1或k2中必然有一个等于maxNode. 所以命题成立
所以假设不成立,k1或k2中必然有一个等于maxNode.所以命题成立
基于上述思路枚举即可。
计算推销员到每一个点的最大开销,找到最大的一个然后累加然后输出。然后再找到下一个最大的开销的点,如果这个点的距离小于之前的点,那么累加A[i]即可,否则累加A[i] + (dis - max)*2
时间复杂度 O ( N 2 ) O(N^{2}) O(N2)
#include<bits/stdc++.h>
#define UP(i,x,y) for(int i=x; i<=y; i++)
#define LEN 100001
using namespace std;
int N, S[LEN]={0}, A[LEN]={0}, X;
int book[LEN]={0};
/* return哪个节点能获得最大的开销 */
int findMaxValue(int maxIndex)
{
int currValue = 0;
int currIndex = 0;
UP(i, 1, N)
{
if(book[i] == 0)
{
/* 新的结点超过前面的结点的距离 */
if(S[i] > S[maxIndex])
{
if(A[i] + (S[i] - S[maxIndex]) * 2 > currValue)
{
currValue = max(A[i] + (S[i] - S[maxIndex]) * 2, currValue);
currIndex = i;
}
}
/* 新的结点小于前面的结点的距离 */
else
{
if(A[i] > currValue)
{
currValue = max(A[i], currValue);
currIndex = i;
}
}
}
}
return currIndex;
}
int main()
{
cin>>N;
UP(i, 1, N)
{
cin>>S[i];
}
UP(i, 1, N)
{
cin>>A[i];
}
int maxIndex = 0;
int k = 0;
int ans = 0;
UP(i, 1, N)
{
k = findMaxValue(maxIndex);
/* 新结点超过了原来最大的结点的距离 */
if(S[k] > S[maxIndex])
{
ans += A[k] + (S[k] - S[maxIndex])*2;
maxIndex = k; // 更新距离最大的结点
}
/* 新结点小于原来最大的结点 */
else
{
ans += A[k];
}
cout<<ans<<endl;
book[k] = 1; // 标记下当前结点走过了
}
return 0;
}
/* 总结:
1.Debug调试十分重要比脑海调试快很多倍
2.设计复杂的代码一定要用函数抽离
*/
2. 100分解法 快排序选择最优点
之前的算法有个问题,就是每次的最优点我们需要枚举 N N N次才能找到最优点,那么可不可以都直接获得最优的点?
我们对每一个点按照开销(距离+疲劳)来从大到小排序,那么第一个元素 i i i就是我们最优的点,将 i i i累加并输出。如果 i i i改变了我们的maxA(已经选择的最远距离),那么我们需要重新排序,又因为 i i i已经选择了,所以排序的范围是 [ i + 1 , N ] [i+1, N] [i+1,N]
时间复杂度。最坏情况下 O ( N ∗ N l o g N ) O(N*NlogN) O(N∗NlogN),最优情况 O ( N l o g N ) O(NlogN) O(NlogN)(也就是第一次选择的是最远的点,sort函数只被调用了一次)
#include<bits/stdc++.h>
#define UP(i,x,y) for(int i=x; i<=y; i++)
#define LEN 100001
using namespace std;
int maxA = 0; // 最远的距离
int N;
struct node{
int dis;
int val;
};
node arr[LEN];
bool cmp(node &a, node &b)
{
int t1, t2;
/* 计算增量 */
if(a.dis > maxA)
{
t1 = a.val + (a.dis - maxA) * 2;
}
else
{
t1 = a.val;
}
/* 计算增量 */
if(b.dis > maxA)
{
t2 = b.val + (b.dis - maxA) * 2;
}
else
{
t2 = b.val;
}
/* 比较 */
if(t1 > t2)
{
return 1;
}
else
{
return 0;
}
}
int main()
{
cin>>N;
UP(i, 1, N)
{
cin>>arr[i].dis;
}
UP(i, 1, N)
{
cin>>arr[i].val;
}
/* i结点的加入能带来多大的开销(距离+疲劳)
按照开销排序
*/
sort(arr+1, arr+N+1, cmp);
int ans = 0;
UP(i, 1, N)
{
/* 新加入的结点距离比maxA远 */
if(arr[i].dis > maxA)
{
ans += (arr[i].dis - maxA) * 2 + arr[i].val; // (arr[i].dis - maxA) 容易错
maxA = arr[i].dis; // 更新最大距离
/*
i结点的加入能带来多大的开销(距离+疲劳)
按照能来多大的开销排序
*/
sort(arr+i+1, arr+N+1, cmp);
}
/* 新加入的结点距离比maxA近 */
else
{
ans += arr[i].val;
}
cout<<ans<<endl;
}
return 0;
}