刚开始看这个题,看到是个贪心就想得特别简单:既然是贪心那肯定是往一个方向走或者直接在两个点之间走来回就行了,所以只需要枚举连续的两个点,然后算一算走到这两个点的距离还有走m个城市的距离和这样加起来就行了。
然而发现事情并不对劲。
直到看到这样一个样例:
1
3 10 2
1 10 14
整个人都不好了!!!
这个的最优解是先走一下1,再回头走10 14来回,这样才是最优解。
所以对于刚刚说的那种贪心以外,还得试试先走反方向再往回走这样的情况,取个最小值就行了。
代码:
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<vector> #include<cstring> #define fs first #define se second #define pb push_back #define cppio ios::sync_with_stdio(false);cin.tie(0) using namespace std; typedef long long ll; typedef pair<int,int> pii; typedef vector<int> VI; const int maxn=1e6+6; const ll inf=0x3f3f3f3f; const ll mod=1e9+7; ll a[maxn]; int main(){ int T; scanf("%d",&T); while(T--){ int n,m,p; scanf("%d%d%d",&n,&m,&p); int pos=0;//记录下p左边有哪些点 for(int i=1;i<=n;i++){ scanf("%lld",a+i); if(p>a[i]){ pos=i; } } ll ans=1e18; for(int i=1;i<n;i++){//枚举i和i+1这两个点走来回 if(i>pos){//当两个点都在pos右边的时候 if(i-pos>m){//如果超过了m个点说明这肯定是无效点了 continue; } ll dis=a[i]-p; ll rem=m-(i-pos); ans=min(ans,dis+rem*(a[i+1]-a[i])); // printf("%lld %d\n",ans,i); if(rem>0&&pos>0){//反向走一走再回头去走来回 ans=min(ans,2*(p-a[pos])+dis+(rem-1)*(a[i+1]-a[i])); } // printf("%lld %d\n",ans,i); } else{ if(pos-i+1>m){//跟上面的同理即可 continue; } ll dis=p-a[i]; ll rem=m-(pos-i+1); ans=min(ans,dis+(a[i+1]-a[i])*rem); if(rem>0&&pos+1<=n){ ans=min(ans,2*(a[pos+1]-p)+dis+(rem-1)*(a[i+1]-a[i])); } // printf("%lld %d\n",ans,i); } } printf("%lld\n",ans); } return 0; }