L . Magical Girl Haze 题目链接: https://nanti.jisuanke.com/t/31001
题解:分层图-最短路(拆点建图),这篇博客写的很详细,包括整个思考的过程----https://www.cnblogs.com/shzr/p/9211128.html
1、将每个点拆成 k+1 个点建立分层图,相当于将原图复制k份,对于第 i 个点,拆成 i,i+n,i+2*n....i+k*n
2、若原图中 i~j有一条权值为 x 的边,则建图如下
add_edge(i,j,x),add_edge(i+n,j+n,x).....add_edge(i+k*n,j+k*n,x)
add_edge(i,j+n,0),add_edge(i+n,j+2*n,0)....add_edge(i+(k-1)*n,j+k*n,0)
3、每向上走一层相当于走了一条权值为0的边,假设走了(i,j+n,0)相当于从第0层走到了第一层,而把i~j这条边的权值改为0。
5、最后输出1~(k+1)*n的最短路即可,至多使得 k 条边为0 ,最优解肯定为选择 k 条边为 0,ans = dis[k*n+n]。
注意:此题卡SPFA、vector邻接表+堆优化的dijstra
AC代码1:
#include
#define ll long long
using namespace std;
const int maxn = 1400010;
const int maxm = 5000010;
const ll inf = 0x3f3f3f3f;
int n,m,k;
ll u[maxm],v[maxm],w[maxm],dis[maxn],first[maxm],NEXT[maxm];
bool vis[maxn];
//用数组模拟邻接表
//first数组用来储存每个顶点其中一条边的编号i,一遍枚举所有的边
//NEXT数组用来储存 "编号为i的边" 的 "前一条边" 的编号
void init()
{
for(int i = 1; i <= n+n*k; i++)
{
dis[i] = inf;
vis[i] = false;
}
memset(first,-1,sizeof(first));
}
void cre_graph()
{
int num = m+1;
for(int i = 1; i <= m; i++)
{
// cin>>u[i]>>v[i]>>w[i];
scanf("%lld%lld%lld",&u[i],&v[i],&w[i]);
NEXT[i] = first[u[i]];//记录某个顶点所访问到的 "前一条边" 的编号
first[u[i]] = i;//不断的覆盖某个顶点所访问到的 "当前的边" 的编号
for(int j = 1; j <= k; j++)
{
u[num] = j*n+u[i];
v[num] = j*n+v[i];
w[num] = w[i];
NEXT[num] = first[u[num]];//记录某个顶点所访问到的 "前一条边" 的编号
first[u[num]] = num;//不断的覆盖某个顶点所访问到的 "当前的边" 的编号
num++;
u[num] = (j-1)*n + u[i];
v[num] = j*n + v[i];
w[num] = 0;
NEXT[num] = first[u[num]];//记录某个顶点所访问到的 "前一条边" 的编号
first[u[num]] = num;//不断的覆盖某个顶点所访问到的 "当前的边" 的编号
num++;
}
}
}
void SPFA(int s)
{
dis[s] = 0;
queue q;
q.push(s);
while(!q.empty())
{
int now = q.front();
q.pop();
for(ll i = first[now]; i != -1; i = NEXT[i])
{
ll to = v[i];
if(dis[to] > w[i] + dis[now])
{
dis[to] = w[i] + dis[now];
q.push(to);
}
}
}
}
int main()
{
// ios::sync_with_stdio(false);
int T;
// cin>>T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
// cin>>n>>m>>k;
if(m <= k)
printf("0\n");
else
{
init();
cre_graph();
int s = 1,e = n;
SPFA(s);
// ll ans = dis[e];
// for(int i = 0; i <= k; i++)
// ans = min(ans, dis[i*n+n]);
// printf("%d\n",ans);
//至多使得k条边为0,最优的情况一定是选择k条边置为0
printf("%d\n",dis[k*n+n]);
}
}
return 0;
}
AC代码2:
#include
using namespace std;
typedef long long ll;
typedef pair iip;
typedef pair llp;
const int MAXN = 2000010;
const int MAXM = 2000010;
const ll INF = 0x3f3f3f3f;
const int MOD = 998244353;
int i, j, n, t, m, k, u, v, c, cnt, head[MAXN];
ll dis[MAXN];
struct edge {
int from;
int to;
int c;
int next;
edge(){};
edge(int _from, int _to, int _c, int _next) : from(_from), to(_to), c(_c), next(_next) {};
}e[MAXM*4];
void init() {
cnt = 0;
for(i = 0; i < MAXN; i++) {
head[i] = -1;
dis[i] = INF;
}
}
void addEdge(int u, int v, int c) {
e[cnt] = edge(u, v, c, head[u]);
head[u] = cnt++;
}
void dij(int s) {
dis[s] = 0;
priority_queue, greater> pq;
pq.push({dis[s], s});
while(!pq.empty()) {
llp now = pq.top();
pq.pop();
int from = now.second;
if(dis[from] < now.first) continue;
for(i = head[from]; i != -1; i = e[i].next) {
int to = e[i].to;
ll cost = e[i].c;
if(dis[from] != INF && dis[to] > dis[from] + cost) {
dis[to] = dis[from] + cost;
pq.push({dis[to], to});
}
}
}
}
int main()
{
scanf("%d", &t);
while(t--) {
scanf("%d%d%d", &n, &m, &k);
init();
for(i = 0; i < m; i++) {
scanf("%d%d%d", &u, &v, &c);
addEdge(u, v, c);
// addEdge(v, u, c);
for(j = 1; j <= k; j++) {
addEdge(u+j*n, v+j*n, c);
// addEdge(v+j*n, u+j*n, c);
addEdge(u+(j-1)*n, v+j*n, 0);
// addEdge(v+(j-1)*n, u+j*n, 0);
}
}
dij(1);
if(k >= m) printf("%lld\n", 0);
else printf("%lld\n", dis[(k+1)*n]);
}
return 0;
}