洛谷 P1772 [ZJOI2006]物流运输

(Ps:好开心好开心自己写对了一道提高+的洛谷题)
首先第一秒想到了今年提高的d1t3(后遗症)
然后开始考虑dp果然有思路:
1,用f[i][j]表示前i天换j次所用的最小时间(不包括换的时间),dis[x]表示到x的最短路径
2,考虑转移:
首先设从第k(1<=k<=i)天到第i天起路线相同,
显然f[i][j]=max(f[i][j],f[k][k-1]+dis[m]*(i-k));
此时从后往前扫,每次把不能用的点加入到flag1里,每次写一个spfa来更新dis
最终答案为max(f[n][i]);(0<=i<n);
时间复杂度大概是O(N^2*(N+E*K));

我用的是spfa求所以是K*E,如果用迪杰斯特拉应该会快点(M^2)(还有堆优化)

献上蒟蒻的ac代码

#include<iostream>
#include<cstdio>  
#include<vector>  
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=110,M=22;
int n,m,K,e,f[N][N],dis[M],flag[M],b[M],c[N][M],tot,flag1[M];
int inf,ans;
struct node { int from,to,last,val; }a[M*M*2];
queue<int>q;

void add(int x,int y,int v)
{
	a[++tot].from=x;
	a[tot].to=y;
	a[tot].last=b[x];
	a[tot].val=v;
	b[x]=tot;
}

void spfa()
{
	int i,j,k;
	memset(dis,0x3f,sizeof(dis));
	inf=dis[1];q.push(1);dis[1]=0;
	while(!q.empty())
	{
	 j=q.front();flag[j]=0;
	 for(i=b[j];i;i=a[i].last)
	  if(!flag1[a[i].to])
	  {
	   if(dis[a[i].to]>(dis[j]+a[i].val))
	   {
	    dis[a[i].to]=dis[j]+a[i].val;
		if(!flag[a[i].to]) flag[a[i].to]=1,q.push(a[i].to);
	   }
	  }
	  q.pop();
	}
}
	   
int main()
{
	freopen("csy.in","r",stdin);
	freopen("csy.out","w",stdout);
	int i,j,k,v,l,r,k1;
	scanf("%d%d%d%d",&n,&m,&K,&e);
	memset(f,0x3f,sizeof(f));
	f[0][0]=0;
	for(i=1;i<=e;++i)
	{
	 scanf("%d%d%d",&j,&k,&v);
	 add(j,k,v);add(k,j,v);
	}
	scanf("%d",&j);
	for(i=1;i<=j;++i)
	{
	 scanf("%d%d%d",&k,&l,&r);
	 for(k1=l;k1<=r;++k1)
	  c[k1][k]=1;
	}
	for(i=1;i<=n;++i)
	{
	 memset(flag1,0,sizeof(flag1));
	 for(j=i;j>=1;--j)
	 {
	  for(k=1;k<=m;++k)
	   flag1[k]=max(c[j][k],flag1[k]);
	  spfa();
	  if(dis[m]!=inf)
	   for(k=1;k<i;++k)
	    f[i][k]=min(f[j-1][k-1]+dis[m]*(i-j+1),f[i][k]);
	  else break;
	 }
	 if(dis[m]!=inf)
	  f[i][0]=dis[m]*i;
	}
	ans=inf;
	for(j=0;j<n;++j)
	 if(f[n][j]<inf)
	  ans=min(ans,f[n][j]+j*K);
	printf("%d\n",ans);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值