Bzoj P1003 [ZJOI2006]物流运输___动态规划+最短路

127 篇文章 0 订阅
30 篇文章 0 订阅

题目大意:

一批货物要从码头 A A A运到码头 B B B需要 n n n天。
希望能够制定一个 n n n天的运输计划。
码头总数为 m m m,编号为 1 1 1~ m m m,码头 A A A编号为 1 1 1,码头 B B B编号为 m m m,每次修改运输路线所需成本为 K K K,有 e e e条航线,每条航线给出其所连接的两个码头 x x x和航线长度,单位长度的运输费用为 1 1 1,航线是双向的。
再给出 d d d对限制 ( P , a , b ) (P,a,b) (P,a,b),表示编号为 P P P的码头在 [ a , b ] [a,b] [a,b]无法装卸货物。
同一个码头有可能在多个时间段内不可用。
保证任何时间都存在至少一条从码头 A A A到码头 B B B的运输路线。
问最小的成本花费总和。

1 &lt; = n &lt; = 100 , 1 &lt; = m &lt; = 20 1&lt;=n&lt;=100,1&lt;=m&lt;=20 1<=n<=1001<=m<=20
1 &lt; P &lt; m , 1 &lt; = a &lt; = b &lt; = n 1 &lt; P &lt; m,1&lt; = a &lt; = b &lt; = n 1<P<m1<=a<=b<=n
航 线 长 度 &gt; 0 航线长度&gt;0 线>0

分析:

c o s t i , j cost_{i,j} costi,j表示在 [ i , j ] [i,j] [i,j]天内使用同一种运输路线的最小花费,
然后可以用 s p f a spfa spfa+前缀和判定限制 预处理出来

然后设 d p i dp_i dpi表示前 i i i天的运输计划的最小成本花费总和。
可以推出
初值: d p i = c o s t 1 , i dp_i=cost_{1,i} dpi=cost1,i
转移: d p i = m a x ( d p i , d p j + c o s t j + 1 , i + K ) dp_i=max(dp_i,dp_j+cost_{j+1,i}+K) dpi=max(dpi,dpj+costj+1,i+K)
最后 d p n dp_n dpn就是答案

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <algorithm>

#define inf 0x7fffff
#define N 105 
#define M 25

using namespace std;

int mincost[N][N], sum[M][N], w[M][M], dis[M], dp[N], n, m, K, e, d;
bool vis[M];

queue <int> Q;

int spfa(int x, int y)
{
	while (Q.size()) Q.pop();
	for (int i = 1; i <= m; i++) dis[i] = inf;
	Q.push(1);
	dis[1] = 0; 
	vis[1] = 1;
	while (!Q.empty())
	{
		int u = Q.front(); Q.pop();
		for (int i = 1; i <= m; i++) 
		    if (sum[i][y] - sum[i][x - 1] == 0 && dis[i] > dis[u] + w[u][i]) 
			{
			    dis[i] = dis[u] + w[u][i];
				if (!vis[i])
				    Q.push(i), vis[i] = 1;	
			} 
		vis[u] = 0;
	}
	return dis[m];
}

int main()
{
	scanf("%d %d %d %d", &n, &m, &K, &e); 
	for (int i = 1; i <= m; i++)
	    for (int j = 1; j <= m; j++) w[i][j] = inf;
	for (int i = 1; i <= e; i++) 
	{
		int x, y, z; scanf("%d %d %d", &x, &y, &z);
		w[x][y] = w[y][x] = z;
	}
	scanf("%d", &d);
	for (int i = 1; i <= d; i++)
	{
		int z, x, y; scanf("%d %d %d", &z, &x, &y);
	    for ( x; x <= y; x++) sum[z][x] = 1; 
	}
	for (int i = 1; i <= m; i++)
	    for (int j = 1; j <= n; j++) sum[i][j] += sum[i][j - 1];
	for (int i = 1; i <= n; i++)
	    for (int j = i; j <= n; j++) mincost[i][j] = (j - i + 1) * spfa(i, j);
	for (int i = 1; i <= n; i++)
	{
		dp[i] = mincost[1][i];
	    for (int j = 1; j < i; j++) 
	        dp[i] = min(dp[i], dp[j] + mincost[j + 1][i] + K);
	}
	printf("%d\n", dp[n]);
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值