POJ3662 [USACO08JAN]Telephone Lines (二分答案/分层图求最短路)
这道题目有两种解法:
1.将每个点视为一个二元组(x,p),表示从起点到x有p条路径免费,相当于构建了一张分层图,N*k个节点,P*k条边。在这张图上用优先队列优化的SPFA算法求解,注意这里的d数组存的不是最短路径,而是路径中边权最大的值,最终答案就是min(d[n][j]),0<=j<=k 。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1005,M=20005; 4 int head[N],to[M],w[M],nxt[M],tot; 5 int n,m,k,d[N][N]; 6 bool v[N][N]; 7 priority_queue< pair<int,pair<int,int> > > q;//加负值变为小根堆 8 9 void add(int x,int y,int z){ 10 nxt[++tot]=head[x]; 11 head[x]=tot; 12 to[tot]=y; 13 w[tot]=z; 14 } 15 16 int main(){ 17 scanf("%d%d%d",&n,&m,&k); 18 for(int i=1;i<=m;i++){ 19 int x,y,z; 20 scanf("%d%d%d",&x,&y,&z); 21 add(x,y,z);add(y,x,z); 22 } 23 memset(d,0x3f,sizeof(d)); 24 d[1][0]=0; 25 q.push(make_pair(0,make_pair(1,0))); 26 while(q.size()){ 27 int i=q.top().second.first,j=q.top().second.second; 28 q.pop(); 29 if(v[i][j]) continue; 30 v[i][j]=true; 31 for(int e=head[i];e;e=nxt[e]){ 32 int y=to[e],z=w[e]; 33 if(d[y][j]>max(d[i][j],z)){//第一种状态转移 34 d[y][j]=max(d[i][j],z); 35 q.push(make_pair(-d[y][j],make_pair(y,j))); 36 } 37 if(j<k && d[y][j+1]>d[i][j]){//第二种状态转移 38 d[y][j+1]=d[i][j]; 39 q.push(make_pair(-d[y][j+1],make_pair(y,j+1))); 40 } 41 } 42 } 43 int ans=0x3f3f3f3f; 44 for(int j=0;j<=k;j++) ans=min(ans,d[n][j]); 45 if(ans==0x3f3f3f3f) puts("-1"); 46 else printf("%d\n",ans); 47 }
2.本题答案具有单调性,可以二分答案求解,转化为判定问题。
对于每个mid,将图中权值>mid的边看做1,权值<=mid的边看做0,然后求1到N的最短路是否不超过k。
对于这种只有0,1边权的图,可以用双端队列BFS求解最短路,0的话放在队头,1的话放在队尾。
调了好久...发现是模板打错了,不能加上if(v[y]) continue; 这样使y不能被进一步更新;而dijkstra可以,因为之前求过的已经是最短路径了,没必要再次更新。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1005,M=20005; 4 int head[N],to[M],w[M],nxt[M],tot; 5 int n,m,k,mx,ans,d[N]; 6 bool v[N]; 7 deque<int> q; 8 9 void add(int x,int y,int z){ 10 nxt[++tot]=head[x]; 11 head[x]=tot; 12 to[tot]=y; 13 w[tot]=z; 14 } 15 16 bool check(int mid){ 17 memset(d,0x3f,sizeof(d)); 18 memset(v,false,sizeof(v)); 19 d[1]=0;v[1]=true; 20 q.push_back(1); 21 while(!q.empty()){ 22 int x=q.front();q.pop_front(); 23 v[x]=false; 24 for(int i=head[x];i;i=nxt[i]){ 25 int y=to[i],z=w[i]>mid?1:0; 26 //if(v[y]) continue;注意加上这句是错误的 27 if(d[y]>d[x]+z){ 28 d[y]=d[x]+z; 29 v[y]=true; 30 if(z==0) q.push_front(y); 31 else q.push_back(y); 32 } 33 } 34 } 35 return d[n]<=k; 36 } 37 38 int main(){ 39 scanf("%d%d%d",&n,&m,&k); 40 for(int i=1;i<=m;i++){ 41 int x,y,z; 42 scanf("%d%d%d",&x,&y,&z); 43 add(x,y,z);add(y,x,z); 44 } 45 int l=0,r=1000001; 46 while(l<r){ 47 int mid=(l+r)>>1; 48 if(check(mid)) r=mid; 49 else l=mid+1; 50 } 51 if(l==1000001) puts("-1"); 52 else printf("%d\n",l); 53 }