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 } 

 

posted @ 2022-07-01 21:41  YHXo  阅读(21)  评论(0编辑  收藏  举报