自己对二分和最短路的思想还是理解不够 太局限于模版 想到该用二分 却不知从何处下手
题目要求设法将起点(1)与终点(n)连接 你可以找出 k 条边使它们的花费为零 而总花费就是这条路径上剩余边中最长边边长
先将边升序排序 二分找最长边 然后跑spfa
在求最短路时 dis[n]数组不再代表两点距离 而是两点之间的路径中有多少条边超过了当前二分的最长边
这样最后的结果 就代表使起点(1)与终点(n)之间超过二分的最长长度的边数量最少 若有不到 k 条边超过当前最长长度 说明我们可以将当前最长长度作为总花费 长度超过当前最长长度的边我们可以使它们免费 而剩下的更短的边不用理会 在之后的不断二分中总会使这 k 条边正好用完
个人小结
二分常与最短路结合 一般看当前条件下(通过二分确定) 能否找到合适的解
若找到了说明我们给的条件还是比较宽松的 在当前基础上还可以再进一步
若找不到说明条件太严苛 不符合实际 应该退一步了
问题的关键就在于怎么找到这个判断条件 这就要靠智商了..
至于最短路 不能只局限于求值 判断个距离大小
比如这道题 最"短"的不再是路径 而是"数量" 即符合条件的对象的数量 符合就是1 否则为0
这种思维抽象在其他算法比如网络流中也有体现
#include <bits/stdc++.h>
using namespace std;
#define N 0x3f3f3f3f
struct node
{
int v;
int w;
int next;
};
node edge[20010];
int first[1010],dis[1010],book[1010],len[10010];
int n,m,k,num,cnt,ans;
void creat();
void addedge(int u,int v,int w);
void binsearch();
void spfa(int lim);
int main()
{
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
creat();
binsearch();
printf("%d\n",ans);
}
return 0;
}
void creat()
{
int pre[10010];
int u,v,w,t,i;
memset(first,-1,sizeof(first));
num=0,t=0;
while(m--)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
t++;
pre[t]=w;
}
sort(pre+1,pre+t+1);
cnt=0;
for(i=1;i<=t;i++)
{
if(pre[i]!=len[cnt])
{
cnt++;
len[cnt]=pre[i];
}
}
len[0]=0;
return;
}
void addedge(int u,int v,int w)
{
edge[num].v=v;
edge[num].w=w;
edge[num].next=first[u];
first[u]=num++;
return;
}
void binsearch()
{
int l,r,mid;
ans=-1,l=0,r=cnt;
while(l<=r)
{
mid=(l+r)/2;
spfa(len[mid]);
if(dis[n]<=k)
{
ans=len[mid];
r=mid-1;
}
else
{
l=mid+1;
}
}
return;
}
void spfa(int lim)
{
queue <int> que;
int i,u,v,w;
memset(dis,N,sizeof(dis));
memset(book,0,sizeof(book));
que.push(1);
dis[1]=0;
book[1]=1;
while(!que.empty())
{
u=que.front();
que.pop();
book[u]=0;
for(i=first[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(edge[i].w>lim) w=1;
else w=0;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if(book[v]==0)
{
que.push(v);
book[v]=1;
}
}
}
}
return;
}