Acwing+3300+食材运输
分析:
-
首先只考虑一辆车,从某一个点出发的情况,它送完所有的酒店的最小时间为:
树形结构中子树(有需要运送的)边权之和*2-最长需要运送的点的长度。
-
暴力枚举处理从第i<n个点出发,运第j种食材的的最小时间。获得一个运送时间表。
- 二分答案,判断mid时间内是否可以完成运送。
- 运送时间表转化:在运送时间表中将可以在mid时间运达的记为1,否则为0。
- 在n行中选择最小的行使得每个食材都可以按时运达。==>状态压缩dp
C++ 代码
#include<bits/stdc++.h>
using namespace std;
const int N=110,M=11,inf=1<<29;
typedef long long ll;
typedef pair<int,int> pii;
#define mk(a,b) make_pair(a,b)
int d[N][M],n,m,k; //d[i][j]从i出发走完食材j的所有点的最小时间,即运送时间表
int tot,head[N],g[N][M] ;
struct Edge{
int v,next,w;
}edge[N*2];
void add(int u,int v,int w) {
edge[++tot]={v,head[u],w};
head[u]=tot;
}
pii dfs(int u,int fa,int op) {
pii res(0,-1);//first表示总时间长,second表示最长的点
if(g[u][op] ) res.second=0;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].v,w=edge[i].w;
if(v==fa) continue;
auto t=dfs(v,u,op);
if(t.second != -1) { //子树中 是否有需要的点
res.first += t.first+w*2;
res.second=max(res.second,t.second+w);
}
} return res;
}
int state[N], f[1<<M];
bool check(int mid) {
memset(state,0,sizeof state);
for(int i=1;i<=n;i++) { //状态
for(int j=0;j<k;j++) {
if(d[i][j]<=mid) state[i] |= 1<<j;
}
}
memset(f,0x3f,sizeof f);
f[0] = 0 ;
for(int i=1;i<=n;i++ ) { //阶段:每个行的选择
for(int j=0;j< 1<<k ; j++){ //状态划分后的位置
f[j|state[i]]=min(f[j|state[i]],f[j]+1); //用或者不用第i行数据
//此时f中状态j为1~i行中可以到达j状态的最小值
}
}
return f[(1<<k)-1]<=m;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=0;j<k;j++) cin>>g[i][j];
for(int i=1;i<n;i++) {
int a,b,c; cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
for(int i=1;i<=n;i++) {
for(int j=0;j<k;j++) {
auto t=dfs(i,-1,j);
if(t.second != -1 )
d[i][j] = t.first-t.second;
}
}
int l=0,r=inf;
while(l<r) {
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<r<<'\n';
return 0;
}