题目要求找出网络中从给定起点到终点的不少于t条的路径,使得路径中相邻结点间的最长路最短。
一般的,解决多结点间的多重最值问题,尤其是最短路中的最长路等问题,都要使用网络流做模型,利用最大流来判断方案的存在性,而其中的最值问题则可能会使用最短路算法或者是贪心,poj2391就用到了最短路,而本题则是用到了“贪心”,即必须先对所有的路按其长度递增排序。然后二分,取标号在mid之前的所有路建图,每条边的容量均为1,求最大流,若最大流的大于等于t,则标号为mid的边的权值即为所求。
一开始时看到有重边就用贪心,任意两个结点只留下最小权值边,结果一直wa,因为存在反例:1->2->4,1->3->4.若前者是重边且有两个权值5,6,后者是单边,有权值7,t=2时,正确答案为6,若按照我之前的做法,则6要排除,故答案为7,错了。所以,网络流中的删边、增边必须要小心。
以下是代码:
- #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=1<<29;
const int M=90020;
const int N=400; - struct node
{
int u,v,w;
int next;
bool operator <(const node a)const
{
return w<a.w;
}
}edge[M];
int head[N],num;
int level[N],queue[2*N];
int cap[N][N],flow[N][N];
int n,p,t; - void init()
{
memset(flow,0,sizeof(flow));
memset(cap,0,sizeof(cap));
} - int dinic_dfs(int star,int end,int ver)
{
int stack[10*N],top=0;
int maxflow=0,cur,minf,ptr;
int i;
stack[top++]=star;
cur=star;
while(top)
{
cur=stack[top-1];
if(cur==end)
{
minf=inf;
for(i=1;i<top;i++)
{
if(minf>cap[stack[i-1]][stack[i]]-flow[stack[i-1]][stack[i]])
{
minf=cap[stack[i-1]][stack[i]]-flow[stack[i-1]][stack[i]];
ptr=i;
}
}
maxflow+=minf;
for(i=1;i<top;i++)
{
flow[stack[i-1]][stack[i]]+=minf;
flow[stack[i]][stack[i-1]]-=minf;
}
top=ptr;
}
else
{
for(i=1;i<=n;i++)
{
if(level[i]==level[cur]+1 && cap[cur][i]>flow[cur][i])
{
stack[top++]=i;
break;
}
}
if(i==n+1)
{
level[cur]=-1;
top--;
}
}
}
return maxflow;
} - bool dinic_bfs(int star,int end,int ver)
{
int queue[10*N],rear=0;
int i,j;
for(i=1;i<=ver;i++)
{
level[i]=-1;
}
queue[rear++]=star;
level[star]=0;
for(i=0;i<rear;i++)
{
for(j=1;j<=n;j++)
{
if(level[j]==-1 && cap[queue[i]][j]>flow[queue[i]][j])
{
level[j]=level[queue[i]]+1;
queue[rear++]=j;
}
}
}
return level[end]>=0;
} - int dinic(int star,int end,int ver)
{
int flow=0,t;
while(dinic_bfs(star,end,ver))
{
t=dinic_dfs(star,end,ver);
if(t) flow+=t;
else break;
}
return flow;
} - bool check(int mid)
{
init();
int s1,t1;
int i;
for(i=0;i<=mid;i++)
{
cap[edge[i].u][edge[i].v]++;
cap[edge[i].v][edge[i].u]++;
}
s1=1;t1=n;
return dinic(s1,t1,n)>=t;
} - int main()
{
scanf("%d%d%d",&n,&p,&t);
int i;
for(i=0;i<p;i++)
{
int a,b;
int c;
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge,edge+p);
int low=0,high=p-1;
int ans=0;
while(low<=high)
{
int mid=(low+high)/2;
if(check(mid))
{
ans=edge[mid].w;
high=mid-1;
}
else low=mid+1;
}
printf("%d/n",ans);
return 0;
}