「美团 CodeM 初赛 Round B」送外卖2

思路:
题目要求最多能送多少个外卖,不妨转换思路,求怎么在相同的送外卖情况下花最少的时间。
这种解法不仅便于设计程序,而且正确性显然:求出这个状态的最小时间,就越有可能转移到能取最优值的那个状态上面。

于是使用利于理解的记忆化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;
}
上一篇:List集合中根据指定个数进行分组,找出分组的和与目标值最接近的组合


下一篇:TT语音:游戏社交乱象难平