题解 luoguP1772 【[ZJOI2006]物流运输】

本文解析了一道结合最短路径与动态规划的竞赛题,通过预处理co[i][j]矩阵来记录任意两天间走最短路的花费,再利用动态规划求解前i天的最小总花费。分享了从理解题意、算法设计到代码实现的全过程,强调了数据类型选择的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模拟赛居然考了这道题,前一天刚看过,结果看了舍不得(不会)做,结果只骗到30pt

讲课人:很容易想到最短路+dpdpdp(我靠一点都不容易)

模拟赛后分析,才知道是处理出第i天到第j天都走同一条最短路的花费为co[i][j]co[i][j]co[i][j]

然后进行dpdpdpdp[i]dp[i]dp[i]表示前i天的最小花费

转移方程很好想:dp[i]=min(dp[j]+co[j+1][i]∗(i−j)+k)dp[i]=min(dp[j]+co[j+1][i]*(i-j)+k)dp[i]=min(dp[j]+co[j+1][i](ij)+k),预处理要赋值为co[1][i]∗ico[1][i]*ico[1][i]i

dpdpdp方程的意思,即在第j+1j+1j+1天改变路线,第j+1j+1j+1天~第iii天都走同一条路线

那么如何处理co[i][j]co[i][j]co[i][j]?

很简单,对于每一个(i,j)(i,j)(i,j),先把iiijjj天之间封闭的码头全部设为不可走,跑一遍最短路即可,初值为无穷

数据辣么小,跑几遍以及跑什么都没关系嘤嘤嘤

那我们就十分愉♂快的解决了此题~~~

愉♂快的提交了然后居然只有90pt

原谅我无耻的打开题解

啊啊啊原来要开longlonglong longlonglong(明明数据辣么小)

献上代码:

#include<bits/stdc++.h>
#define soo (1e8)
#define ll long long
using namespace std;
int d,cnt,head[25],dis[25],vis[25],cant_vis[25];
ll co[105][105],dp[105];
int n,m,k,ee,cl[25][105];
struct Edge{
	int v,nx,s;
}e[10005];
inline int read(){
    int ret=0,ff=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') ff=-ff;ch=getchar();}
    while(isdigit(ch)){ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
    return ret*ff;
}
void add(int x,int y,int z){
	e[++cnt].v=y;
	e[cnt].s=z;
	e[cnt].nx=head[x];
	head[x]=cnt;
}
void spfa(){//爱跑什么跑什么
	for(int i=1;i<=m;i++) dis[i]=soo,vis[i]=0;
	queue<int> q;
	dis[1]=0;
	q.push(1);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		vis[x]=0;
		for(int i=head[x];i;i=e[i].nx){
			int v=e[i].v;
			if(cant_vis[v]) continue;
			if(dis[v]>dis[x]+e[i].s){
				dis[v]=dis[x]+e[i].s;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
}
signed main(){
	n=read(),m=read(),k=read(),ee=read();
	for(int i=1;i<=ee;i++){
		int x=read(),y=read(),z=read();
		add(x,y,z);
		add(y,x,z);
	}
	d=read();
	for(int i=1;i<=d;i++){
		int t=read(),x=read(),y=read();
		for(int j=x;j<=y;j++) cl[t][j]=1;
	}
	//cl[i][j]表示第i个码头在第j天不能走
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			memset(cant_vis,0,sizeof(cant_vis));
			for(int r=i;r<=j;r++)
				for(int l=1;l<=m;l++)
					if(cl[l][r]) cant_vis[l]=1;
			spfa();
			co[i][j]=dis[m];
		}
	memset(dp,0x7f,sizeof(dp));
	for(int i=1;i<=n;i++){
		dp[i]=(ll)co[1][i]*i;
		for(int j=i-1;j>=0;j--)
			dp[i]=min(dp[i],dp[j]+co[j+1][i]*(i-j)+k);
	}
	printf("%lld",dp[n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值