保留道路
【 问题描述 】
很久很久以前有一个国家,这个国家有N个城市,城市由1,2,3,…,N标号,城市间有M条双向道路,每条道路都有两个属性g和s,两个城市间可能有多条道路,并且可能存在将某一城市与其自身连接起来的道路。后来由于战争的原因,国王不得不下令减小花费从而关闭一些道路,但是必须要保证任意两个城市相互可达。
道路花费的计算公式为wG*max{所有剩下道路的属性g}+wS*max{所有剩下道路的属性s},其中wG和wS是给定的值。国王想要在满足连通性的前提下使这个花费最小,现在需要你计算出这个花费。
【 输入格式 】
输入文件名为road.in。
第一行包含两个正整数N和M。
第二行包含两个正整数wG和wS。
后面的M行每行描述一条道路,包含四个正整数u,v,g,s,分别表示道路连接的两个
城市以及道路的两个属性。
【 输出格式 】
输出文件名为road.out。
输出一个整数,表示最小花费。若无论如何不能满足连通性,输出-1。
【 输入输出样例 】
road.in road.out
3 3
2 1
1 2 10 15
1 2 4 20
1 3 5 1
30
【 数据规模与约定 】
对于10%的数据,N≤10,M≤20;
对于30%的数据,N≤100,M≤1000;
对于50%的数据,N≤200,M≤5000;
对于100%的数据,N≤400,M≤50000,wG,wS,g,s≤1000000000。
/* 二维费用的最小生成树,直接没思路,搜索也没打,最多10分 以下是题解给出的做法,不过多赘述了: { 30 分做法: 按照 g 属性从小到大排序,枚举 maxG,对满足 maxG 的所有道路 按照 s 属性从小到大排序,然后做 kruskal,时间复杂度O(M^2logM)。 50 分做法: 在 30 分基础上,发现每次只增加一条边,插入到上次的边集合中再做 kruskal 即可,时间复杂度 O(M^2)。 100 分做法: 依旧按照 g 属性从小到大排序。丌断加入新边的过程中发现,当前的最小生成树只可能是由未加入新边的最小生成树的边和当前新边组成的共 N 条边中选出 N-1 条构成。因此维护一个最小生成树边集,每次只在 N 条边中做最小生成树,时间复杂度 O(MN)。 } 但由此得到一些经验: ①关于二维费用的其它问题,可以先考虑枚举一维费用,在这基础上使第二维费用最小。 ②剪枝真的很重要,以后能多减就多减。 */ #include<cstdio> #include<iostream> #include<algorithm> #define N 410 #define M 50010 #define INF 100000000000000000LL #define ll long long using namespace std; int n,m,Ws,Wg,cnt,fa[N]; ll ans=INF; struct node { int x,y;bool vis; ll g,s; };node e[M],tree[M],zan[M]; int read() { int num=0,flag=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();} while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();} return num*flag; } bool cmp(const node&s1,const node&s2) { if(s1.g!=s2.g)return s1.g<s2.g; return s1.s<s2.s; } int find(int x) { if(fa[x]==x)return x; return fa[x]=find(fa[x]); } void small(ll maxg) { for(int i=1;i<=cnt;i++)zan[i]=tree[i],zan[i].vis=false; ll maxs=0,tot=0; for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=cnt;i++) { int a=find(tree[i].x),b=find(tree[i].y); if(a!=b) { maxs=max(maxs,tree[i].s); fa[a]=b; tot++; zan[i].vis=1; } if(tot==n-1) { int p=0; for(int i=1;i<=cnt;i++) if(zan[i].vis)tree[++p]=zan[i]; cnt=p; ans=min(ans,maxg+maxs); break; } } } int main() { freopen("jh.in","r",stdin); //freopen("road.in","r",stdin); //freopen("road.out","w",stdout); n=read();m=read();Wg=read();Ws=read(); for(int i=1;i<=m;i++) { e[i].x=read();e[i].y=read();e[i].g=(ll)read();e[i].s=(ll)read(); e[i].g*=(ll)Wg;e[i].s*=(ll)Ws; } sort(e+1,e+m+1,cmp); for(int i=1;i<=m;i++) { if(e[i].g+e[i].s>ans)continue;//剪枝太重要了,感觉自己太弱了 int pos=cnt+1; for(int j=1;j<=cnt;j++) if(tree[j].s>e[i].s) { pos=j; break; } if(pos==cnt+1) { ++cnt; tree[cnt]=e[i]; } else { ++cnt; for(int j=cnt;j>=pos+1;j--) tree[j]=tree[j-1]; tree[pos]=e[i]; } small(e[i].g); } cout<<ans; return 0; }