2018 icpc-南京网络赛

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;
}

 

你可能感兴趣的:(2018 icpc-南京网络赛)