3 条题解

  • 0
    @ 2025-8-28 16:26:06

    灾后重建 题解

    luogu P1772物流运输~~~~~~↑

    首先,求最短路选择什么方法。第一眼看到数据范围n<=20,这么小,我就想去用Floyd,然后就彻底把自己绕进去了。事实上也不是不能用Floyd实现(不确定能不能过),记录一下这个村庄维修的时间,然后在第一层for循环内判一下是否能通行就可以了。

    然而还是用dijkstra吧...施工的话直接记录下来,后面跑dij的时候vis标记一下就解决了。

    f[i]表示前i天的代价最小值,转移方程f[i]=min(f[i],f[j]+(i-j)*dis[j+1][i]+c);,表示前j天的代价+第j+1到第i天换一条新路的代价。题解都有讲,在此不多赘述。这里主要记录一下自己的思考。能力过蒻所以很多地方不能一下想到。

    比如说我一开始没有想到开一个dis[i][j]表示第i到第j天都能通行的一条最短路。不过确实有往这方面感性理解。不能直接找全局最短路,因为中间可能不通;也不能每一次都去找最短路,这样总是会有额外的换路费用c,至此可想到用dp了。

    还是感性理解一下,怎么样才能保证最优?就是路既比较短又不用经常换,这个“不用经常换”启发我枚举时间作为dp转移的中间点,至此可想到设计f[i]表示前i天的代价最小值了,且最短路要与时间有关。

    设想一种情况,假设这是最后一次换路了,则前面需要保证最优的条件就只需要满足“路比较短”了,那我们就只需要找一条从现在开始到最后时间都能走的最短路就可以了,至此想到设计dis[i][j]表示第i天到第j天都能通行的最短路长度,整个解题思路清晰了。

    那么dij()里需要传两个参表示初始与结束时间,利用这个我们就能把提前记录过的不能通行的点的vis打标记了。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=107,M=207;
    int D,n,c,m,u,v,w,k,tot,head[M],vis[27],d[57],cons[27][N];
    ll f[N],dis[N][N];
    priority_queue<pair<int,int> > q;
    struct node{
    	int to,nxt,w;
    }e[2*M];
    void add(int x,int y,int z){
    	e[++tot].to=y;
    	e[tot].w=z;
    	e[tot].nxt=head[x];
    	head[x]=tot;
    }
    void clr(){
    	memset(vis,0,sizeof vis);
    	memset(d,0x3f,sizeof d);
    }
    int dij(int st,int ed){
    	clr();
    	for(int i = 1;i<=n;i++)
    		for(int j = st;j<=ed;j++)
    			if(cons[i][j])vis[i]=1;
    	d[1]=0;
    	q.push({0,1});
    	while(!q.empty()){
    		int x=q.top().second;
    		q.pop();
    		if(vis[x])continue ;
    		vis[x]=1;
    		for(int i = head[x];i;i=e[i].nxt){
    			int y=e[i].to,w=e[i].w;
    			if(d[y]>d[x]+w){
    				d[y]=d[x]+w;
    				q.push({-d[y],y});
    			}
    		}
    	}
    	return d[n];
    }
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    	memset(f,0x3f,sizeof f);
    	cin>>D>>n>>c>>m;
    	for(int i = 1;i<=m;i++){
    		cin>>u>>v>>w;
    		add(u,v,w);add(v,u,w);
    	}
    	cin>>k;
    	while(k--){
    		cin>>u>>v>>w;
    		for(int i = v;i<=w;i++)cons[u][i]=1;//construction
    	}
    	for(int i = 1;i<=D;i++)
    		for(int j = i;j<=D;j++)
    			dis[i][j]=dij(i,j);//i~j天的最短路
    	f[0]=-c;
    	for(int i = 1;i<=D;i++)
            for(int j = 0;j<i;j++)
                f[i]=min(f[i],f[j]+(i-j)*dis[j+1][i]+c);  
    	cout<<f[D]<<'\n';
    	return 0;
    }
    

    开不开long long都见**啊最好开一部分 调了半天一直是负数。。

    信息

    ID
    359
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    (无)
    递交数
    32
    已通过
    10
    上传者