hdu1839(二分+优先队列,bfs+优先队列与spfa的区别)
题意:有n个点,标号为点1到点n,每条路有两个属性,一个是经过经过这条路要的时间,一个是这条可以承受的容量。现在给出n个点,m条边,时间t;需要求在时间t的范围内,从点1到点n可以承受的最大容量........
思路:其实我是觉得思路挺简单的,就是二分枚举每条边的容量,然后再看在这个容量的限制下,是否可以从点1到点n........
方法1:二分枚举边的容量,然后一次dfs,判断在容量和时间的双重限制下,是否可以从点1到达点n......
wa代码:
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<algorithm> using namespace std; typedef __int64 ss; struct node { ss k; ss t; ss v; }; ss n,m,t,a[50005],sum,flag; vector<node>vet[10005]; ss vist[10005]; void dfs(ss x,ss maxn,ss total) { if(x==n) { flag=1; return; } if(flag==1) return; for(ss i=0;i<vet[x].size();i++) { node p=vet[x][i]; if(!vist[p.k]&&p.v>=maxn&&(p.t+total<=t)) { vist[p.k]=1; dfs(p.k,maxn,p.t+total); } } } ss deal(ss num) { ss maxn=a[num]; flag=0; memset(vist,0,sizeof(vist)); dfs(1,maxn,0); return flag; } int main() { int text; scanf("%d",&text); while(text--) { ss cnt=0; scanf("%I64d%I64d%I64d",&n,&m,&t); for(int i=0;i<=n;i++) vet[i].clear(); for(ss i=0;i<m;i++) { ss v1,v2,tmp,tmp1; scanf("%I64d %I64d %I64d %I64d",&v1,&v2,&tmp,&tmp1); node p; p.k=v2; p.t=tmp1; p.v=tmp; vet[v1].push_back(p); p.k=v1; vet[v2].push_back(p); a[cnt++]=tmp; } sort(a,a+cnt); //printf("%I64d\n",cnt); ss ll=0,rr=cnt-1; ss ans=0; while(ll<=rr) { sum=0; ss mid=(ll+rr)/2; if(deal(mid)) { if(ans<a[mid]) ans=a[mid]; ll=mid+1; } else rr=mid-1; } printf("%I64d\n",ans); } return 0; }
我倒是很快明白了过来,错误在什么地方。因为我的dfs是每个点只历遍一次,有的路被忽略掉了,从而导致wa,额,可以改改,让dfs回溯,然后重置vist标记数组,但是这样做的话,肯定超时...<..>
方法2:二分枚举边的容量,然后用优先队列+bfs,判断在容量和时间的双重限制下,是否可以从点1到达点n......
wa代码:
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<algorithm> using namespace std; typedef __int64 ss; struct node { ss k; ss t; ss v; }; struct node1 { friend bool operator<(const node1 a,const node1 b) { if(a.t>b.t) return 1; else return 0; } ss e; ss t; }; ss n,m,t,a[500005],sum,flag; vector<node>vet[100005]; ss vist[100005]; /*void dfs(ss x,ss maxn,ss total) { if(x==n) { flag=1; return; } if(flag==1) return; for(ss i=0;i<vet[x].size();i++) { node p=vet[x][i]; if(!vist[p.k]&&p.v>=maxn&&(p.t+total<=t)) { vist[p.k]=1; dfs(p.k,maxn,p.t+total); vist[p.k]=0; } } }*/ priority_queue<node1>q; ss bfs(ss num) { ss minx=a[num]; node1 p; memset(vist,0,sizeof(vist)); p.e=1; p.t=0; while(!q.empty()) q.pop(); q.push(p); vist[p.e]=1; while(!q.empty()) { p=q.top(); q.pop(); vist[p.e]=0; if(p.e==n) { if(p.t<=t) { return 1; } return 0; } ss x=p.e; for(ss i=0; i<vet[x].size(); i++) { node p1=vet[x][i]; node1 iter; iter.e=p1.k; iter.t=p1.t+p.t; if(!vist[p1.k]&&p1.v>=minx&&iter.t<=t) { vist[p1.k]=1; q.push(iter); } } } return 0; } int main() { int text; scanf("%d",&text); while(text--) { ss cnt=0; scanf("%I64d%I64d%I64d",&n,&m,&t); for(ss i=0; i<=n; i++) vet[i].clear(); for(ss i=0; i<m; i++) { ss v1,v2,tmp,tmp1; scanf("%I64d %I64d %I64d %I64d",&v1,&v2,&tmp,&tmp1); node p; p.k=v2; p.t=tmp1; p.v=tmp; vet[v1].push_back(p); p.k=v1; vet[v2].push_back(p); a[cnt++]=tmp; } sort(a,a+cnt); //printf("%I64d\n",cnt); ss ll=0,rr=cnt-1; ss ans=0; while(ll<=rr) { //sum=0; ss mid=(ll+rr)/2; if(bfs(mid)) { if(ans<a[mid]) ans=a[mid]; ll=mid+1; } else rr=mid-1; } printf("%I64d\n",ans); } return 0; }
思考了很久,也是明白了为什么会wa。在以往,我使用bfs、bfs+优先队列求最小值、最短路什么的时候,都是在一张点图上求解的。换句话说,那样的图上,一个点到另一个点可以到达,那么它们必然相邻,然后路径值固定是1,当然也许会有从某个点到另一个点,路径不是1的,假设从这个点到那个点的距离是k,那么从其他相邻点到那个点的距离也是k,如此就可以使得bfs搜索过去的每个状态只需更新一次,因为更新完一次必然是最小的。
但是,这个题目却是不同。因为它并不是点有权值,而是边的权值,假如我从点1到点3这个点权值为9,那么从点2到点3的权值可以为3......如此就会导致搜索过去,更新了的状态并不是最小的........
这就是bfs与spfa的区别吧,bfs可以实现的时候,必然是点自身带权值,比如说,点1有两个属性,一个是xx,一个是yy,要是从其他可以到达点1的点,那么必须加上这个点的某个权值,然后求最小值........
而spfa却是边带权值.........
方法3:二分+spfa
ac代码:
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<algorithm> using namespace std; typedef __int64 ss; struct node { ss k; ss t; ss v; }; ss n,m,t,a[50005],sum,flag; vector<node>vet[10005]; ss vist[10005],dis[10005]; ss spfa(ss num) { ss minx=a[num]; for(ss i=0; i<=n; i++) { dis[i]=((ss)1<<25); vist[i]=0; } dis[1]=0; vist[1]=1; queue<ss>q; q.push(1); while(!q.empty()) { ss x=q.front(); q.pop(); vist[x]=0; for(ss i=0; i<vet[x].size(); i++) { node p=vet[x][i]; if(p.v>=minx) { if(dis[p.k]>dis[x]+p.t) { dis[p.k]=dis[x]+p.t; if(!vist[p.k]) q.push(p.k); vist[p.k]=1; } } } } if(dis[n]<=t) return 1; else return 0; } int main() { int text; scanf("%d",&text); while(text--) { ss cnt=0; scanf("%I64d%I64d%I64d",&n,&m,&t); for(ss i=0; i<=n; i++) vet[i].clear(); for(ss i=0; i<m; i++) { ss v1,v2,tmp,tmp1; scanf("%I64d %I64d %I64d %I64d",&v1,&v2,&tmp,&tmp1); node p; p.k=v2; p.t=tmp1; p.v=tmp; vet[v1].push_back(p); p.k=v1; vet[v2].push_back(p); a[cnt++]=tmp; } sort(a,a+cnt); //printf("%I64d\n",cnt); ss ll=0,rr=cnt-1; ss ans=0; while(ll<=rr) { //sum=0; ss mid=(ll+rr)/2; if(spfa(mid)) { if(ans<a[mid]) ans=a[mid]; ll=mid+1; } else rr=mid-1; } printf("%I64d\n",ans); } return 0; }