思路:
题目要求最多能送多少个外卖,不妨转换思路,求怎么在相同的送外卖情况下花最少的时间。
这种解法不仅便于设计程序,而且正确性显然:求出这个状态的最小时间,就越有可能转移到能取最优值的那个状态上面。
于是使用利于理解的记忆化dfs,记录当前点是哪个,各包裹的状态(0/1/2,0表示没动,1表示拿在手上,2表示送完),然后再图上乱走,只要能标1就标1,标了1以后才能标2。在这个求到达各状态的min时间的同时记录最大的送外卖数。
难点还是在状态和转移的设计上。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=21;
int n,m,q;
int f[N][59050*3];
int bin[11];
int g[N][N];
int s[N],t[N],l[N],r[N];
int ans,xx;
int get (int x)
{
int tt=0;
for (int u=10;u>=0;u--){
if (x>=bin[u]*2) tt++;
while (x>=bin[u]) x-=bin[u];
}
return tt;
}
int check (int x,int y)//x的第y位是0,1,2中的哪一个{
for (int u=10;u>y;u--)
while (x>=bin[u])
x=x-bin[u];
if (x>=bin[y]*2) return 2;
if (x>=bin[y]) return 1;
return 0;
}
void dfs (int now,int x,int z)//现在在哪一个点,物品的状态,现在的时间
{
for (int u=1;u<=q;u++){
if (s[u]==now&&check(x,u)==0&&z>=l[u])//这里可取
x=x+bin[u];
}
for (int u=1;u<=q;u++){
if (t[u]==now&&check(x,u)==1&&z<=r[u])//这里可放
x=x+bin[u];
}
if (f[now][x]<=z) return ;
f[now][x]=z;
ans=max(ans,get(x));
if (z>=xx) return ;
for (int u=1;u<=q;u++)
{
if (check(x,u)==0) dfs(s[u],x,max(z+g[now][s[u]],l[u]));//速速去取
if (check(x,u)==1) dfs(t[u],x,z+g[now][t[u]]);//速速去放
}
}
int main(){
bin[0]=1;
for(int u=1;u<=10;u++)
bin[u]=bin[u-1]*3;
memset(f,127,sizeof(f));
memset(g,63,sizeof(g));
scanf("%d%d%d",&n,&m,&q);
for (int u=1;u<=n;u++) g[u][u]=0;
for (int u=1;u<=m;u++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
g[x][y]=min(g[x][y],z);
}
for (int k=1;k<=n;k++)//求最短路,k循环在最外层
for (int u=1;u<=n;u++)
for (int i=1;i<=n;i++)
g[u][i]=min(g[u][i],g[u][k]+g[k][i]);
for (int u=1;u<=q;u++){
scanf("%d%d%d%d",&s[u],&t[u],&l[u],&r[u]);
xx=max(xx,r[u]);
}
dfs(1,0,0);
printf("%d\n",ans);
return 0;
}