首先看到有 T<=1e5 T <= 1 e 5 次询问。那么是有个预处理,最后通过一个差不多log的复杂度能得出答案。
dp[i][j]表示从i出发,有j的钱,最远能走多少。
如果能得出这个,那么最后之后二分钱数,找一个最小的钱满足距离要求就可以了。
然后是转移,只需要在加油的点上转移,
dp[i][j]=max(dp[i][j],dp[k][j−p[i]]+mp[i][k])
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
k
]
[
j
−
p
[
i
]
]
+
m
p
[
i
]
[
k
]
)
,
mp[i][k]
m
p
[
i
]
[
k
]
表示从i出发,最多走c[i]条边到k,走过最大的距离是多少。因为到k点就要重新加油,所以每次肯定尽量把油用光。
mp[i][k]
m
p
[
i
]
[
k
]
可以用倍增来维护
先维护一个
dis[i][j][k]
d
i
s
[
i
]
[
j
]
[
k
]
表示从i走到j,最多走
2k
2
k
步,走的最远距离是多少。
那么用dis来组合出mp就可以了,因为我们可以把
c[i]
c
[
i
]
变成二进制,每一位1都代表了一个步数。
for (int i=1;i<=n;i++)
{
int cc = min(C, c[i]);
for (int j=1;j<=n;j++) mp[i][j] = tmp[i][j] = -INF;
mp[i][i] = 0;
for (int l=0;l<20;l++) if ( cc & (1<<l) )
{
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
tmp[i][j] = max(tmp[i][j], mp[i][k] + dis[k][j][l]);
for (int j=1;j<=n;j++) mp[i][j] = tmp[i][j];
}
}
找到新的L的时候,必须全部用旧的mp来更新,否则这次的步数会和上次混在一起,可能会出现这次的步数走了两次的情况。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 110;
const int INF = 0x3f3f3f3f;
int dis[MAXN][MAXN][22],p[MAXN],c[MAXN],dp[MAXN][MAXN*MAXN];
int mp[MAXN][MAXN],tmp[MAXN][MAXN];
int n,m,C,T;
int sov(int s,int q,int d)
{
int l=1,r=q,ans = -1;
while (l<=r)
{
int mid = (l+r)>>1;
if ( dp[s][mid] >= d )
{
ans = mid;
r = mid-1;
}
else l = mid+1;
}
return ans;
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d%d%d%d",&n,&m,&C,&T);
memset(dis,0xc2,sizeof dis);
for (int i=1;i<=n;i++) scanf("%d%d",&p[i],&c[i]),dis[i][i][0] = 0;
for (int i=1;i<=m;i++)
{
int a,b,l;
scanf("%d%d%d",&a,&b,&l);
dis[a][b][0] = max(dis[a][b][0],l);
}
for (int i=1;i<=17;i++)
for (int a=1;a<=n;a++)
for (int b=1;b<=n;b++)
for (int c=1;c<=n;c++)
dis[a][b][i] = max(dis[a][b][i], dis[a][c][i-1] + dis[c][b][i-1]);
for (int i=1;i<=n;i++)
{
int cc = min(C, c[i]);
for (int j=1;j<=n;j++) mp[i][j] = tmp[i][j] = -INF;
mp[i][i] = 0;
for (int l=0;l<20;l++) if ( cc & (1<<l) )
{
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
tmp[i][j] = max(tmp[i][j], mp[i][k] + dis[k][j][l]);
for (int j=1;j<=n;j++) mp[i][j] = tmp[i][j];
}
}
int qmax = n*n;
for (int i=1;i<=qmax;i++)
for (int j=1;j<=n;j++)
if ( i >= p[j] )
for (int k=1;k<=n;k++)
dp[j][ i ] = max( dp[j][i], dp[k][ i - p[j] ] + mp[j][k] );
for (int i=1;i<=T;i++)
{
int s,q,d;
scanf("%d%d%d",&s,&q,&d);
int tmp = sov(s,q,d);
if (tmp == -1)
printf("-1\n");
else printf("%d\n",q - tmp);
}
return 0;
}