大视野:http://www.lydsy.com/JudgeOnline/problem.php?id=1003
1003: [ZJOI2006]物流运输
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 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
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
//前三天走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);
}