50%的做法:
先按照s升序排序。
从小到大枚举maxg,把g小于maxg的边全部选出来,(因为前面已经排过序了),造一棵最小生成树,更新答案。时间复杂度≈O(m*m)。
100分的做法:
按照g升序排序。
维护一个n-1条边的边集,是上一个建造的最小生成树的边集。
从前往后枚举maxg,把这条边按照s用插入排序插入到当前n-1条边的集合中。
在这样的n条边的集合中建造一颗最小生成树,最后再把用到的边存到边集中(就是将没用到的删去),从而维护了一个n-1的边集。
刚才所说的边集其实就是维护的一棵最小生成树。
时间复杂度≈O(m*n)。
因为最小生成树的性质:在当前的最小生成树中插入一条边,构成了环,把环中最长的边删去就是新的最小生成树。
60分代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<map>
#define LL long long
#define N 50009
using namespace std;
int n,m,f[509];
LL Ws,Wg,ans=(1ll*1<<62),order[N];
struct H{int u,v;LL s,g;}b[N];
LL maxg,maxs,num;
bool cmp(H x,H y)
{
return x.s<y.s;
}
void init()
{
for(int i=1;i<=n;i++) f[i]=i;
}
int find(int x)
{
if(x==f[x])return x;
return f[x]=find(f[x]);
}
int main()
{
scanf("%d%d%lld%lld",&n,&m,&Wg,&Ws);
for(int i=1;i<=m;i++)
{
scanf("%d%d%lld%lld",&b[i].u,&b[i].v,&b[i].g,&b[i].s);
b[i].s*=Ws;b[i].g*=Wg;order[i]=b[i].g;
}
sort(order+1,order+m+1);
sort(b+1,b+m+1,cmp);
for(int i=n-1;i<=m;i++)
{
init();
maxg=order[i];num=n;
for(int j=1;j<=m;j++)
if(b[j].g<=maxg)
{
int fx=find(b[j].u),fy=find(b[j].v);
if(fx!=fy)
{
num--;
f[fx]=fy;
maxs=b[j].s;
}
if(num==1) break;
}
if(num==1) ans=min(ans,maxs+maxg);
}
if(ans==(1ll*1<<62))printf("-1
");
else printf("%lld",ans);
return 0;
}
100分做法:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<map>
#define LL long long
#define N 50009
using namespace std;
int n,m,f[509],cnt,num;
LL ans=(1ll*1<<62);
LL Ws,Wg,order[N],r;
struct H{
int u,v;
LL s,g;
}b[N],q[N],tree[N];
bool used[N];
LL maxg,maxs;
bool cmp(H x,H y)
{
return x.g<y.g;
}
int find(int x)
{
if(x==f[x])return x;
return f[x]=find(f[x]);
}
void get_ans(LL G)
{
maxg=G;maxs=0;num=n;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=cnt;i++) q[i]=tree[i],used[i]=0;
for(int i=1;i<=cnt;i++)
{
int fx=find(q[i].u),fy=find(q[i].v);
if(fx!=fy)
{
maxs=max(maxs,q[i].s);
f[fx]=fy;
num--;
used[i]=1;
}
}
if(num==1)
{
int p=0;
ans=min(ans,maxs+maxg);
for(int i=1;i<=cnt;i++)
{
if(used[i])
tree[++p]=q[i];
}
cnt=p;
}
}
int main()
{
scanf("%d%d%lld%lld",&n,&m,&Wg,&Ws);
for(int i=1;i<=m;i++)
{
scanf("%d%d%lld%lld",&b[i].u,&b[i].v,&b[i].g,&b[i].s);
b[i].g*=Wg;b[i].s*=Ws;
}
sort(b+1,b+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(b[i].g+b[i].s>ans) continue;//这个剪枝可以减少很多多余的计算
int pos=cnt+1;
for(int j=1;j<=cnt;j++)
if(tree[j].s>b[i].s)
{
pos=j;break;
}
if(pos==cnt+1) tree[++cnt]=b[i];
else
{
++cnt;
for(int j=cnt;j>pos;j--)
tree[j]=tree[j-1];
tree[pos]=b[i];
}
if(cnt<n-1) continue;
get_ans(b[i].g);
}
if(ans==(1ll*1<<62)) printf("-1");
else printf("%lld",ans);
return 0;
}