【[ZJOI2006]物流运输】

一直不会做,觉得这是一道神题

于是万般无奈下去借鉴抄了一下题解

发现这就是一道套路题

我们用\(dp[i]\)表示前\(i\)天的最小总花费,于是我们就可以用一个常规的老套路来做了

那就是枚举断点

我们如果可以预处理出一个数组\(dis[i][j]\)表示在第\(i\)天到第\(j\)天的最短路的话,方程是不是就很好写了

\(dp[i]=min(dp[j]+k+dis[j+1][i]*(i-j))\)

但是我们这个数组怎么预处理呢

瞎跑最短路就可以了

反正数据范围这么小,基本什么算法都能过去了,对于\(dijkstra\),把那些不能走的点标记成已经找到了最短路,让这些点没有办法去做松弛操作就好了

这种断点类dp的题还是不少的,比如说经典的山区建小学,在比如说那道统计单词个数都是比较套路的枚举断点类dp,这种dp的复杂度一般来说是\(O(n^2)\)的,枚举断点的时候也非常无脑,所以对于这种dp,应该尽量保证见到就能A掉

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<bitset>
#define mp make_pair
#define re register
#define maxn 101
#define inf 999999999
#define LL long long
using namespace std;
struct GG
{
	int v,nxt,w;
}e[10001];
LL n,m,k,E,Q,num;
typedef pair<LL,int> pii;
priority_queue<pii,vector<pii>,greater<pii> > q;
LL d[maxn],head[maxn];
bitset<maxn> f;
LL dp[101];
int a[101][101],tot[101];
LL dis[101][101];
inline LL read()
{
	char c=getchar();
	LL x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
	  x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
inline void add_edge(int x,int y,int z)
{
	e[++num].v=y;
	e[num].nxt=head[x];
	e[num].w=z;
	head[x]=num;
}
inline LL dijkstra(int beg,int enn)
{
	for(re int i=1;i<=m;i++) 
		d[i]=inf,f[i]=0;
	for(re int i=beg;i<=enn;i++)
	{
		for(re int j=1;j<=tot[i];j++)
			f[a[i][j]]=1;
	}
	d[1]=0;
	q.push(mp(0,1));
	while(!q.empty())
	{
		int k=q.top().second;
		q.pop();
		if(f[k]) continue;
		f[k]=1;
		for(re int i=head[k];i;i=e[i].nxt)
		if(d[e[i].v]>d[k]+e[i].w)
		{
			d[e[i].v]=d[k]+e[i].w;
			q.push(mp(d[e[i].v],e[i].v));
		}
	}
	return d[m];
}
int main()
{
	n=read();
	m=read();
	k=read();
	E=read();
	LL x,y,z;
	for(re int i=1;i<=E;i++)
	{
		x=read();
		y=read();
		z=read();
		add_edge(x,y,z);
		add_edge(y,x,z);
	}
	Q=read();
	while(Q--)
	{
		z=read();
		x=read();
		y=read();
		for(re int i=x;i<=y;i++)
			a[i][++tot[i]]=z;
	}
	for(re int i=1;i<=n;i++)
	for(re int j=i;j<=n;j++)
		dis[i][j]=dijkstra(i,j);
	for(re int i=1;i<=n;i++)
	{
		dp[i]=i*dis[1][i];
		for(re int j=1;j<i;j++)
		dp[i]=min(dp[i],dp[j]+dis[j+1][i]*(i-j)+k);
	}	
	cout<<dp[n]<<endl;
	return 0;
}
posted @ 2019-01-02 12:03  asuldb  阅读(109)  评论(0编辑  收藏  举报