P1828 [USACO3.2]香甜的黄油 Sweet Butter 题解
简要题意:
给定一个 \(n\) 个点的无向图,有边权。有 \(k\) 个仓库,每个点属于一个仓库。
你需要选定 \(k\) 个仓库其中的一个使得所有点到它的最短路上的边权之和最小。求这个最小的和。
\(n \leq 800\).
一看到 \(n \leq 800\),算法很明显。有两种解决方案。
算法一
利用 \(\text{Floyd}\).
可以用 \(\mathcal{O}(n^3)\) 的时间求出两两最短路。
下面大力统计即可。
时间复杂度:\(\mathcal{O}(n^3)\).
实际得分:\(0pt\).
算法二
利用 \(\text{SPFA}\)$.
同样是求两两点最短,考虑 \(n\) 次单源,用 \(\text{SPFA}\) 做到 \(\mathcal{O}(n^3)\).
实际上这东西跑不满。
时间复杂度:\(\mathcal{O}(n^3)\).
实际得分:\(100pts\).(不可能跑满啊)
#include<bits/stdc++.h>
using namespace std;
inline int read(){char ch=getchar();int f=1; while(!isdigit(ch)) {if(ch=='-')f=-f; ch=getchar();}
int x=0;while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
int n,p,c;
vector<pair<int,int> >G[2001];
int dis[2001],vis[2001],a[2001];
inline int SPFA(int stc) { //SPFA 单源求出所有点到 stc 的最短路
queue<int>q;
memset(dis,0x3f,sizeof(dis)); dis[stc]=0; vis[stc]=1;
q.push(stc);
while(!q.empty()) {
int u=q.front(); q.pop(); vis[u]=0;
for(int i=0;i<G[u].size();i++) {
int v=G[u][i].first,c=G[u][i].second;
if(dis[v]>dis[u]+c) {
dis[v]=dis[u]+c;
if(!vis[v]) {
vis[v]=1; q.push(v);
}
}
}
}
int s=0;
for(int i=1;i<=n;i++) s+=dis[a[i]];
return s; //统计当前仓库的答案
}
int main(){
n=read(),p=read(),c=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=c;i++) {
int u=read(),v=read(),w=read();
G[u].push_back(make_pair(v,w));
G[v].push_back(make_pair(u,w));
}
int mini=INT_MAX;
for(int i=1;i<=p;i++) mini=min(mini,SPFA(i));
printf("%d\n",mini); //最小答案
return 0;
}
算法三
考虑数据加强:\(n \leq 2000\).
同样求两两最短路,用 \(n\) 次 \(\text{Dijkstra}\) 即可做到 \(\mathcal{O}(n^2 \log n)\).
时间复杂度:\(\mathcal{O}(n^2 \log n)\).
实际得分:\(100pts\).
简易的代码胜过复杂的说教。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步