「LibreOJ NOIP Round #1」旅游路线

Description

T 城是一个旅游城市,具有 nnn 个景点和 mmm 条道路,所有景点编号为 1,2,...,n1,2,...,n1,2,...,n。每条道路连接这 nnn 个景区中的某两个景区,道路是单向通行的。每条道路都有一个长度。
为了方便旅游,每个景点都有一个加油站。第 iii 个景点的加油站的费用为 pip_ip​i​​,加油量为 cic_ic​i​​。若汽车在第 iii 个景点加油,则需要花费 pip_ip​i​​ 元钱,之后车的油量将被加至油量上限与 cic_ic​i​​ 中的较小值。不过如果加油前汽车油量已经不小于 cic_ic​i​​,则不能在该景点加油。
小 C 准备来到 T 城旅游。他的汽车油量上限为 CCC。旅游开始时,汽车的油量为 000。在旅游过程中:
1、当汽车油量大于 000 时,汽车可以沿从当前景区出发的任意一条道路到达另一个景点(不能只走道路的一部分),汽车油量将减少 111;
2、当汽车在景点 iii 且当前油量小于 cic_ic​i​​ 时,汽车可以在当前景点加油,加油需花费 pip_ip​i​​ 元钱,这样汽车油量将变为 min{ci,C}\min{c_i,C}min{c​i​​,C}。
一次旅游的总花费等于每次加油的花费之和,旅游的总路程等于每次经过道路的长度之和。注意多次在同一景点加油,费用也要计算多次,同样地,多次经过同一条道路,路程也要计算多次。
小 C 计划旅游 TTT 次,每次旅游前,小 C 都指定了该次旅游的起点和目标路程。由于行程不同,每次出发前带的钱也不同。为了省钱,小 C 需要在旅游前先规划好旅游路线(包括旅游的路径和加油的方案),使得从起点出发,按照该旅游路线旅游结束后总路程不小于目标路程,且剩下的钱尽可能多。请你规划最优旅游路线,计算这 TTT 次旅游每次结束后最多可以剩下多少钱。

solution

看到 \(dis>=10^9\)\(n<=100\) 这种东西,要想到倍增Floyed,首先要发现我们不能把油记录在dp状态里面否则开不下,我们考虑枚举加油位置.
我们定义 \(dp[i][j]\) 表示从 \(i\)出发,在j加满油,花费 \(j\) 元钱可以走的最大距离,答案就是满足 \(dp[i][j]>=d\) 的最大j,所以我们要想办法预处理出这个,然后询问就可以快速回答了,\(dp[i][j]=Max(dp[k][j-p[i]]+dis[j][k])\)\(dis[i][j]\) 表示从 \(i\) 出发达到 \(j\),经过道路数不超过 \(Min(c[i],C)\) 的最长距离,可以倍增Floyd预处理出来,然后dp数组具有二分性,可以二分回答询问,但是我爆枚也过了,最坏10^9,常数太小了,哎...

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define RG register
using namespace std;
typedef long long ll;
const int N=105,M=2005;
const ll inf=2e15;
int n,m,C,T,p[N],c[N];ll w[N][N][20];
ll f[N],g[N],dis[N][N],dp[N][N*N];

void work()
{
	scanf("%d%d%d%d",&n,&m,&C,&T);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&p[i],&c[i]);
		if(c[i]>C)c[i]=C;
	}
	int x,y,z;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=0;k<=18;k++)
				w[i][j][k]=-inf;
	for(int i=1;i<=n;i++)w[i][i][0]=0;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		w[x][y][0]=Max(w[x][y][0],z);
	}
	for(int k=1;k<=18;k++)
		for(int l=1;l<=n;l++)
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					w[i][j][k]=Max(w[i][j][k],w[i][l][k-1]+w[l][j][k-1]);
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)
			g[j]=(i==j?0:-inf),f[j]=-inf;

		for(int k=18;k>=0;k--){
			if(((1<<k)&c[i])==0)continue;
			for(int j=1;j<=n;j++){
				for(int l=1;l<=n;l++)
					f[j]=Max(f[j],g[l]+w[l][j][k]);
			}
			for(int j=1;j<=n;j++)g[j]=f[j];
		}
		for(int j=1;j<=n;j++)dis[i][j]=g[j];
	}

	int lim=n*n;
	for(int k=0;k<=lim;k++){
		for(int i=1;i<=n;i++){
			if(k<p[i])continue;
			for(int j=1;j<=n;j++)
				dp[i][k]=Max(dp[i][k],dp[j][k-p[i]]+dis[i][j]);
		}
	}
	while(T--){
		scanf("%d%d%d",&x,&y,&z);
		int ans=y+1;
		for(int i=0;i<=y;i++){
			if(dp[x][i]>=z){ans=i;break;}
		}
		printf("%d\n",y-ans);
	}
}

int main()
{
	freopen("trip.in","r",stdin);
	freopen("trip.out","w",stdout);
	work();
	return 0;
}

posted @ 2017-11-07 15:51  PIPIBoss  阅读(380)  评论(0编辑  收藏  举报