传送门

题意

\(n\) 个点,已知坐标 \(x_i\),参数 \(a_i,b_i,c_i,d_i\)。可以根据坐标和参数计算从一个点跳到另一个点的花费。
要求从 \(s\) 点到 \(t\) 点且经过所有仅一次的最小总花费。

思路

第一次接触这样贪心的题,感觉很巧妙
做一个链表,最开始为 \(s\)->\(e\)
然后从 \(1\)\(n\) 依次插入链表,(\(s\)\(e\) 除外)
至于插到哪个位置,因为 \(k\) 插到 \(i\)->\(j\) 中,花费增加为 \(len[i][k]+len[k][j]-len[i][j]\)
那么找这个最小的增加花费的位置就是要插入的位置了

代码

不想写链表,于是直接写了个vector,

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const LL INF=0x3f3f3f3f3f3f3f3f;
int n,s,e,x[5010],a[5010],b[5010],c[5010],d[5010];
LL len[5010][5010],ans;
vector<int> vec;


int main(){
    scanf("%d%d%d",&n,&s,&e);
    for(int i=1;i<=n;i++) scanf("%d",&x[i]);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<=n;i++) scanf("%d",&d[i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(i==j) continue;
            if(j<i) len[i][j]=(LL)abs(x[i]-x[j])+c[i]+b[j];
            else len[i][j]=(LL)abs(x[i]-x[j])+d[i]+a[j];
        }
    vec.push_back(s);
    vec.push_back(e);
    ans=len[s][e];
    for(int i=1;i<=n;i++){
        if(i==s||i==e) continue;
        int pos=1;
        LL temp=ans-len[vec[0]][vec[1]]+len[vec[0]][i]+len[i][vec[1]];
        for(int j=2;j<vec.size();j++){
            LL tp=ans-len[vec[j-1]][vec[j]]+len[vec[j-1]][i]+len[i][vec[j]];
            if(tp<temp) pos=j,temp=tp;
        }
        ans=ans-len[vec[pos-1]][vec[pos]]+len[vec[pos-1]][i]+len[i][vec[pos]];
        vec.insert(vec.begin()+pos,i);
    }
    cout<<ans<<endl;
    return 0;
}
12-19 15:05