P4568 [JLOI2011]飞行路线
题意:给你一张无向图,现在你可以把图中至多条边的边权变为0.求此时的最短路。
解法:最短路+DP(还有个更为熟知的标签叫做分层图emmm)
设为从
用了
条免费边后的最短路,那么:
由状态转移:此时边k被作为免费边使用。
由状态转移:很显然这是一个最短路。
其中指所有终点为
的边的序号。
Code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#define ri register int
using namespace std;
const int MAXN=100020,KMAXN=55,INF=1310668019;
int n,m,k,s,t,u[MAXN],v[MAXN],w[MAXN],fst[MAXN],nxt[MAXN],dis[MAXN][KMAXN],book[MAXN];
inline int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||'9'<ch) ch=getchar();
while('0'<=ch&&ch<='9')
{
x=(x <<3)+(x <<1)+(int)(ch-'0');
ch=getchar();
}
return x;
}
struct node{
int num,c;
};
priority_queue<node>Q;
bool operator<(const node &a,const node &b)
{
return a.c > b.c;
}
void Dijkstra(int cnt)//cnt:当前使用的免费机会的次数
{
memset(book,0,sizeof(book));
for(ri i=0;i<=n-1;i++) dis[i][cnt]=INF;
dis[s][cnt]=0;
Q.push((node){s,0});
while(!Q.empty())
{
int x=Q.top().num; Q.pop();
if(book[x]) continue;
book[x]=1;
for(ri k=fst[x];k>0;k=nxt[k])
{
if(cnt==0&&dis[v[k]][cnt]>dis[u[k]][cnt]+w[k])
//cnt=0时跑出来的dis[][cnt]应为原图的最短路
{
dis[v[k]][cnt]=dis[u[k]][cnt]+w[k];
Q.push((node){v[k],dis[v[k]][cnt]});
}
if(cnt>0)
//cnt>0时dis[v[k]][cnt]可由dis[u[k]][cnt-1]和dis[u[k]][cnt]+w[k]这两种状态转移而来。需分类讨论。
{
if(dis[v[k]][cnt]>dis[u[k]][cnt-1])
{
dis[v[k]][cnt]=dis[u[k]][cnt-1];
Q.push((node){v[k],dis[v[k]][cnt]});
}
if(dis[v[k]][cnt]>dis[u[k]][cnt]+w[k])
{
dis[v[k]][cnt]=dis[u[k]][cnt]+w[k];
Q.push((node){v[k],dis[v[k]][cnt]});
}
}
}
}
}
int main()
{
n=read(),m=read(),k=read(),s=read(),t=read();
m <<=1;
for(ri i=1;i<=m;i+=2)
{
u[i]=read(),v[i]=read(),w[i]=read();
nxt[i]=fst[u[i]],fst[u[i]]=i;
u[i+1]=v[i],v[i+1]=u[i],w[i+1]=w[i];
nxt[i+1]=fst[u[i+1]],fst[u[i+1]]=i+1;
}
for(ri i=0;i<=k;i++)
//dis[][i]从dis[][i-1]和dis[][i]转移而来,故跑k+1遍dijk。这边跑完后更新出dis[][i]。
//这样不断更新知道更新完 dis[][k],dis[n][k]即为答案。
Dijkstra(i);
cout<<dis[t][k];
return 0;
}