2018-南京网络赛icpc-L题(分层最短路)
题意:给你n个点,m条边的有向带权图,然后你每次可以选<=k条边的边权变成0,问你1到n的最短路;
解题思路:这道题基本上就是原题了呀,bzoj2763(无向图),解法就是拆点跑分层的最短路,比如这道题,你用了一次变为0,就相当于进入了下一个层次;
我们把每个点都拆成k个层次点,每个相同层次的点按输入的边权连接,每个点可以向它能连接到的点的下一个层次连接一条边权为0的边;
意思就是:输入x到y权值为w的边
for(int i=1;i<=m;i++) { scanf("%lld%lld%lld",&x,&y,&w); for(int j=0;j<=k;j++) { add(x+j*n,y+j*n,w);同一层次的点; if(j!=k) add(x+j*n,y+(j+1)*n,0);不同层次的点,可以向它能连接的下一个层次连一个边权为0; } }
完整代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #include<stack> #define ll long long using namespace std; const int maxm=3000500; const ll inf=0x7f7f7f7f; const int maxn=100500; struct node { ll num; ll dist; node(ll _num,ll _dist):num(_num),dist(_dist){} friend bool operator<(node a,node b) { return a.dist>b.dist; } }; struct Edge { ll next; ll to; ll w; }edge[maxm*2]; int head[maxm]; ll dist[maxm]; int cnt; int n,m,k; void add(ll u,ll v,ll w) { edge[cnt].next=head[u]; edge[cnt].to=v; edge[cnt].w=w; head[u]=cnt++; } void dij(int u) { priority_queue<node>q; memset(dist,inf,sizeof(dist)); dist[u]=0; q.push(node(u,dist[u])); while(!q.empty()) { node now=q.top(); //cout<<now.num<<" "<<now.dist<<endl; q.pop(); int x=now.num; for(int i=head[x];i!=-1;i=edge[i].next) { int v=edge[i].to; if(dist[v]>dist[x]+edge[i].w) { dist[v]=dist[x]+edge[i].w; q.push(node(v,dist[v])); } } } } int main() { int tt; ll x,y,w; scanf("%d",&tt); while(tt--) { memset(head,-1,sizeof(head));cnt=0; scanf("%d%d%d",&n,&m,&k); if(k>=m) { printf("0\n"); continue; } for(int i=1;i<=m;i++) { scanf("%lld%lld%lld",&x,&y,&w); for(int j=0;j<=k;j++) { add(x+j*n,y+j*n,w); if(j!=k) add(x+j*n,y+(j+1)*n,0); } } dij(1); ll ans=inf; for(int i=0;i<=k;i++) { ans=min(ans,dist[n+i*n]); } printf("%lld\n",ans); } }