题解 P5096 [USACO04OPEN]Cave Cows 1
这题给的数据范围也太弱了吧,为啥 k 给得像状压一样……
题意简述
-
\(n\) 个点 \(m\) 条边的无向图,边有边权,只有当体积小于边权时才能通过。
-
有 \(k\) 个特殊点上放有食物,吃掉可以使体积加一(经过该点时可以选择不吃)。
-
初始时体积为 \(0\) ,要求从 \(1\) 号点出发,最后回到 \(1\) 号点,可以重复经过一条边。求最多能吃掉多少食物。
-
\(n \leq 100\),\(m \leq 1000\),\(k \leq 14\)。
题目分析
记 \(w(x,y)\) 表示能从 \(x\) 走到 \(y\) 的最大体积。
由于边可以重复经过,事实上每一个特殊点可以独立考虑。
也就是说,贝茜的路线一定可以变为:从 \(1\) 号点出发,到达某个特殊点并吃掉食物,再回到 \(1\) 号点,并将这个过程重复 \(ans\) 次。
理由很简单。假设贝茜走到了特殊点 \(x\) 吃掉食物后体积为 \(p\),又走到特殊点 \(y\) 吃掉食物,那么有 \(w(x,y) \geq p, w(y,1)\geq p+1\)(否则就再也走不回 \(1\) 号点了)。
因此 \(w(x,1) \geq p\),将 \(x \rightarrow y\) 变为 \(x \rightarrow 1 \rightarrow y\) 是合法的。
那么接下来的事情就是算出 \(w(1,t)\),然后每一次贪心选取合法且 \(w(1,t)\) 最小的特殊点吃掉。这一步的排序需要 \(O(k\log k)\) 的时间复杂度。
考虑对每个点 \(t\) 计算 \(w(1,t)\),显然这样的路径一定在最大生成树上,建出树 dfs 一次即可。这一步最小生成树视实现需要 \(O(m \log m)\) 或 \(O(m \log n)\) 的时间复杂度。
总时间复杂度 \(O(k \log k+m \log m)\) 或 \(O(k \log k+m \log n)\),可以非常非常轻松地通过本题。
直接最优解……
代码
#include<bits/stdc++.h>
using namespace std;
const int N=110,M=1100;
struct nod{
int to,nxt,w;
}e[N*2];
int head[N],cnt;
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int n,m,k;
int p[15],q[15];
int dis[110];
struct abc{
int u,v,w;
}edg[M*2];
bool operator <(abc x,abc y){
return x.w>y.w;
}
int fa[N];
int getf(int x){
if(x==fa[x]) return x;
return fa[x]=getf(fa[x]);
}
bool merge(int x,int y){
int fx=getf(x),fy=getf(y);
if(fx==fy) return 0;
fa[fx]=fy;
return 1;
}
void dfs(int u,int fa,int mn){
dis[u]=mn;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa) continue;
dfs(v,u,min(mn,e[i].w));
}
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=k;i++) scanf("%d",&p[i]);
for(int i=1;i<=m;i++) scanf("%d%d%d",&edg[i].u,&edg[i].v,&edg[i].w);
sort(edg+1,edg+m+1);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
int u=edg[i].u,v=edg[i].v;
if(merge(u,v)) add(u,v,edg[i].w),add(v,u,edg[i].w);
}
//最大生成树
dfs(1,0,1e9);
for(int i=1;i<=k;i++) q[i]=dis[p[i]]-1;
sort(q+1,q+k+1);
int nw=0,ans=0;//现在的体积 / 答案
for(int i=1;i<=k;i++)
if(nw<=q[i]) nw++,ans++;//贪心
cout<<ans;
}