NOIP2013 货车运输
题目描述
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入输出格式
输入格式:
输入文件名为 truck.in。
输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。 接下来 m 行每行 3 个整数 x、 y、 z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号 城市有一条限重为 z 的道路。注意: x 不等于 y,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出格式:
输出文件名为 truck.out。
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
输入输出样例
输入样例#1:
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出样例#1:
3
-1
3
数据范围
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000;
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。
暴力30分:
读入q之后直接将每一个读入的x点当做源点,跑spfa,用x更新每一个点,最后输出源点到终点y的距离即可。
这里的spfa中,将最短路改成了求所有路径中,使到达终点的边权值的最小值最大:dis[v]=min(edge[i].val,dis[u]);就是用min(到达该路径起点的最小限重,该路径的限重)来更新下一个点。
1 #include<cstdio> 2 #include<queue> 3 using namespace std; 4 const int maxn=1e4+5,maxm=5e4+5,inf=2147483647; 5 int head[maxn],dis[maxn]; 6 bool inq[maxn]; 7 int num,n,m; 8 queue<int> q; 9 struct cp{ 10 int to,val,next; 11 }edge[maxm<<1]; 12 inline void add(int u,int v,int w){ 13 edge[++num].to=v; 14 edge[num].val=w; 15 edge[num].next=head[u]; 16 head[u]=num; 17 } 18 int min(int a,int b){ 19 return a<b?a:b; 20 } 21 int max(int a,int b){ 22 return a>b?a:b; 23 } 24 void spfa(int s){ 25 for(int i=1;i<=n;i++) dis[i]=-1,inq[i]=0; 26 while(!q.empty()) q.pop(); 27 q.push(s); 28 dis[s]=inf; 29 inq[s]=1; 30 while(!q.empty()){ 31 int u=q.front(); 32 inq[u]=0; 33 q.pop(); 34 for(int i=head[u];i;i=edge[i].next){ 35 int v=edge[i].to; 36 if(dis[v]<min(edge[i].val,dis[u])){ 37 dis[v]=min(edge[i].val,dis[u]); 38 if(!inq[v]){ 39 q.push(v); 40 inq[v]=1; 41 } 42 } 43 } 44 } 45 } 46 inline int read(){ 47 char ch=getchar(); 48 int x=0,f=1; 49 while(ch<'0'||ch>'9'){ 50 if(ch=='-') f=-1; 51 ch=getchar(); 52 } 53 while(ch>='0'&&ch<='9'){ 54 x=(x<<1)+(x<<3)+(ch^48); 55 ch=getchar(); 56 } 57 return x*f; 58 } 59 int main(){ 60 n=read(),m=read(); 61 int x,y,z; 62 while(m--){ 63 x=read(),y=read(),z=read(); 64 add(x,y,z),add(y,x,z); 65 } 66 int q=read(); 67 while(q--){ 68 x=read(),y=read(); 69 spfa(x); 70 eprintf("%d\n",dis[y]); 71 } 72 return 0; 73 }
暴力60分:
因为我们是使路径中的最小限重最大,则如果我们建一颗最大生成树,则从x到y的路径一定是最优的(因为既然可以从x到达y,那么肯定是先按照大的边走答案才会最优)。
其中spfa部分不变,只是建一颗最大生成树将边的数目降到 n 的级别上,这样从每个点开始乱跑也可以混过去60分。
1 #include<cstdio> 2 #include<queue> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 const int maxn=1e4+5,maxm=5e4+5,inf=2147483647; 7 int head[maxn],dis[maxn],f[maxn]; 8 bool inq[maxn]; 9 int num,n,m; 10 queue<int> q; 11 struct cp{ 12 int to,val,next; 13 }edge[maxm<<1]; 14 struct cpp{ 15 int u,v,w; 16 }e[maxm<<1]; 17 bool cmp(cpp a,cpp b){ 18 return a.w>b.w; 19 } 20 inline void add(int u,int v,int w){ 21 edge[++num].to=v; 22 edge[num].val=w; 23 edge[num].next=head[u]; 24 head[u]=num; 25 } 26 int min(int a,int b){ 27 return a<b?a:b; 28 } 29 int max(int a,int b){ 30 return a>b?a:b; 31 } 32 int find(int x){ 33 if(f[x]==x) return x; 34 return f[x]=find(f[x]); 35 } 36 void Union(int a,int b){ 37 int x=find(a),y=find(b); 38 f[x]=y; 39 } 40 void spfa(int s){ 41 for(int i=1;i<=n;i++) dis[i]=-1,inq[i]=0; 42 while(!q.empty()) q.pop(); 43 q.push(s); 44 dis[s]=inf; 45 inq[s]=1; 46 while(!q.empty()){ 47 int u=q.front(); 48 inq[u]=0; 49 q.pop(); 50 for(int i=head[u];i;i=edge[i].next){ 51 int v=edge[i].to; 52 if(dis[v]<min(edge[i].val,dis[u])){ 53 dis[v]=min(edge[i].val,dis[u]); 54 if(!inq[v]){ 55 q.push(v); 56 inq[v]=1; 57 } 58 } 59 } 60 } 61 } 62 inline int read(){ 63 char ch=getchar(); 64 int x=0,f=1; 65 while(ch<'0'||ch>'9'){ 66 if(ch=='-') f=-1; 67 ch=getchar(); 68 } 69 while(ch>='0'&&ch<='9'){ 70 x=(x<<1)+(x<<3)+(ch^48); 71 ch=getchar(); 72 } 73 return x*f; 74 } 75 int main(){ 76 n=read(),m=read(); 77 int x,y,z; 78 for(int i=1;i<=n;i++) f[i]=i; 79 for(int i=1;i<=m;i++) e[i].u=read(),e[i].v=read(),e[i].w=read(); 80 sort(e+1,e+m+1,cmp); 81 for(int i=1;i<=m;i++){ 82 x=e[i].u,y=e[i].v,z=e[i].w; 83 if(find(x)!=find(y)){ 84 add(x,y,z),add(y,x,z); 85 Union(x,y); 86 } 87 } 88 int q=read(); 89 while(q--){ 90 x=read(),y=read(); 91 spfa(x); 92 printf("%d\n",dis[y]); 93 } 94 return 0; 95 }
非暴力100分:
既然我们建了最大生成树了,那为什么不直接用lca来求==
在这里我用dis数组表示从x到y路径中的最小值,最后输出答案的时候用solve函数判断一下即可。
1 #include<algorithm> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #define ll long long 6 #define dd double 7 using namespace std; 8 const int maxn=1e4+5,maxm=5e4+5,inf=0x7ffffff; 9 int n,m,q,num,m_dep,s; 10 int f[maxn],head[maxn],edge[maxn]; 11 int dep[maxn]={-1},fa[maxn][20],dis[maxn][20]; 12 bool inq[maxn]; 13 struct cp{ 14 int u,v,w; 15 }a[maxm]; 16 struct cpp{ 17 int to,nxt,w; 18 }e[maxn<<1]; 19 inline void add(int u,int v,int w){ 20 e[++num]=(cpp){v,head[u],w}; 21 head[u]=num; 22 e[++num]=(cpp){u,head[v],w}; 23 head[v]=num; 24 } 25 inline int max(int a,int b){return a>b?a:b;} 26 inline int min(int a,int b){return a<b?a:b;} 27 inline void swap(int &x,int &y){x^=y^=x^=y;} 28 bool cmp(cp x,cp y){return x.w>y.w;} 29 int find(int x){ 30 if(f[x]==x) return x; 31 return f[x]=find(f[x]); 32 } 33 void Union(int a,int b){ 34 int x=find(a),y=find(b); 35 f[x]=y; 36 } 37 void dfs(int x){ 38 inq[x]=1; 39 for(int i=head[x];i;i=e[i].nxt){ 40 int v=e[i].to; 41 if(inq[v]) continue; 42 dep[v]=dep[x]+1; 43 m_dep=max(m_dep,dep[v]); 44 fa[v][0]=x; 45 dis[v][0]=e[i].w; 46 dfs(v); 47 } 48 } 49 inline void prepare(){ 50 s=log2(m_dep); 51 for(int i=1;i<=s;i++) for(int j=1;j<=n;j++) 52 fa[j][i]=fa[fa[j][i-1]][i-1], 53 dis[j][i]=min(dis[j][i-1],dis[fa[j][i-1]][i-1]); 54 } 55 inline int lca(int x,int y){ 56 if(dep[x]<dep[y]) swap(x,y); 57 if(dep[x]!=dep[y]) for(int i=s;i>=0;i--) 58 if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; 59 if(x==y) return x; 60 for(int i=s;i>=0;i--) if(fa[x][i]!=fa[y][i]) 61 x=fa[x][i],y=fa[y][i]; 62 return fa[x][0]; 63 } 64 inline int solve(int x,int y){ 65 int ans=inf; 66 for(int i=s;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) 67 ans=min(ans,dis[x][i]),x=fa[x][i]; 68 return ans; 69 } 70 inline int read(){ 71 char ch=getchar(); 72 int x=0,f=1; 73 while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} 74 while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar(); 75 return x*f; 76 } 77 void write(int x){ 78 if(x<0) x=-x,putchar('-'); 79 if(x>9) write(x/10); 80 putchar(x%10^48); 81 } 82 int main(){ 83 int cnt=0; 84 n=read(),m=read(); 85 memset(dis,63,sizeof dis); 86 for(int i=1;i<=n;i++) f[i]=i; 87 for(int i=1;i<=m;i++) a[i].u=read(),a[i].v=read(),a[i].w=read(); 88 sort(a+1,a+m+1,cmp); 89 for(int i=1;i<=m;i++){ 90 int x=a[i].u,y=a[i].v; 91 if(find(x)!=find(y)) 92 Union(x,y),add(x,y,a[i].w),++cnt; 93 if(cnt==n-1) break; 94 } 95 for(int i=1;i<=n;i++) if(!inq[i]) dfs(i); 96 prepare(); 97 q=read(); 98 while(q--){ 99 int x=read(),y=read(); 100 if(find(x)!=find(y)) write(-1); 101 else{ 102 int t=lca(x,y); 103 write(min(solve(x,t),solve(y,t))); 104 } 105 putchar('\n'); 106 } 107 return 0; 108 }
在这里,特别感谢hzwer(黄学长),他的博客写的真的特别好,有很多模板和题目在他的博客里都有,这道题目也是通过黄学长的博客才A掉的,大家如果还有什么看不懂的地方的话,也可以参考一下黄学长的博客 →→→ hzwer(货车运输)。