bzoj 1003 [ZJOI2006]物流运输(最短路spfa+dp)

大视野:http://www.lydsy.com/JudgeOnline/problem.php?id=1003

1003: [ZJOI2006]物流运输

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 8303  Solved: 3489
[Submit][Status][Discuss]

Description

  物流公司要把一批货物从码头A运到码头B。由于货物量比较大,需要n天才能运完。货物运输过程中一般要转
停好几个码头。物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种
因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。但是
修改路线是一件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个n天的运输计划,使得总成本
尽可能地小。

Input

  第一行是四个整数n(1<=n<=100)、m(1<=m<=20)、K和e。n表示货物运输所需天数,m表示码头总数,K表示
每次修改运输路线所需成本。接下来e行每行是一条航线描述,包括了三个整数,依次表示航线连接的两个码头编
号以及航线长度(>0)。其中码头A编号为1,码头B编号为m。单位长度的运输费用为1。航线是双向的。再接下来
一行是一个整数d,后面的d行每行是三个整数P( 1 < P < m)、a、b(1< = a < = b < = n)。表示编号为P的码
头从第a天到第b天无法装卸货物(含头尾)。同一个码头有可能在多个时间段内不可用。但任何时间都存在至少一
条从码头A到码头B的运输路线。

Output

  包括了一个整数表示最小的总成本。总成本=n天运输路线长度之和+K*改变运输路线的次数。

Sample Input

5 5 10 8
1 2 1
1 3 3
1 4 2
2 3 2
2 4 4
3 4 1
3 5 2
4 5 2
4
2 2 3
3 1 1
3 3 3
4 4 5

Sample Output

32
//前三天走1-4-5,后两天走1-3-5,这样总成本为(2+2)*3+(3+2)*2+10=32
【心得】:

刚开始做bzoj的题,超级难。但是每个题都会收获颇多。虽然搜到的题解都说这是水题,我希望我在进步。

最后AC的代码还是看了题解才会的,其实我很不想看题解再做。可我真的做不出来。

从中午12点,到现在7点了,A掉这道题。

先说说我一直wrong的思路,我觉得是可行的,可就是A不掉。若有大神留意到我的思路,请指点。


我用spfa求得这个图的最短k优路

(就是bfs过程保存所有可能的解,代码上我只保存到k=400,因为k太大会超内存)

然后对这个数组进行dp,(区间限制的天数我用树状数组维护的)

状态方程是dp[i][j]=min( dp[i-1][j], min(dp[i-1][t])+k ) 其中t在1到k之间,且t!=j;

i是第几天,j是第几短的路。

意思是,当前状态的选择:1、继续走前一天的路。2、换一条路,同时加k元改路费

示例是能运行出来的。不知道这种想法对不对。


【解析】:

用spfa把所有的天数区间[i,j]的最短路全跑出来,即最[i,j]天内,有些点被限制,跑最短路时标记出来,不走。

把数据存进fuck[i][j]。

然后dp。有那么一点像最长递增子序列的做法

数组dp[i]表示前i天所用最少费用。

【代码】:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
struct edge{
	int s,t,len;
	int next;
}E[400];
int head[25];
void adde(int s,int t,int len)//加边
{
	static int cnt=0;
	E[cnt]={s,t,len,head[s]};
	head[s]=cnt++;
}
int n,m,k,e,d,u,v,l;
int c[1000][3];//存区间 
int dis[25],p[25],fuck[110][110];
int dp[110];
int spfa(int s,int t)
{
	queue<int>q;
	q.push(s);
	memset(dis,INF,sizeof(dis));
	dis[s]=0;
	while(!q.empty())
	{
		int a=q.front();q.pop();
		for(int i=head[a];~i;i=E[i].next)
		{
			int b=E[i].t;
			if(p[b]&&dis[b]>dis[a]+E[i].len)
			{
				dis[b]=dis[a]+E[i].len;
				q.push(b);
			}
		}
	}
	return dis[t];
}
int solve()
{
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++)//p记录这段时间内能走的点 
		{
			memset(p,true,sizeof(p));
			for(int t=0;t<d;t++)
				if(c[t][1]<=j&&c[t][2]>=i)//被限制了
					p[c[t][0]]=false; 
			fuck[i][j]=spfa(1,m);
		}
	memset(dp,INF,sizeof(dp));
	dp[0]=0;//假设一个第0天 
	for(int i=1;i<=n;i++)//第i填
	{
		for(int j=0;j<i;j++)
		{
			if(dp[j]!=INF&&fuck[j+1][i]!=INF)
			dp[i]=min(dp[i],dp[j]+fuck[j+1][i]*(i-j)+k);
		}
	}
	return dp[n]-k;
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&k,&e);
	memset(head,-1,sizeof head);
	for(int i=0;i<e;i++)
	{
		scanf("%d%d%d",&u,&v,&l);
		adde(u,v,l);
		adde(v,u,l);
	}
	scanf("%d",&d);
	memset(c,0,sizeof(c));
	for(int i=0;i<d;i++)
	{
		for(int j=0;j<3;j++)
			scanf("%d",&c[i][j]);//存限制的区间	
	}
	int ans=solve();
	printf("%d\n",ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪的期许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值