【CSP】202012-4 食材运输 70% 一点思路
对于K==M的情况,问题重点是:如何统计从某点出发,遍历需要某食材的所有酒店最小权重和。
考虑到N规模很小,因此可以直接枚举从每个点出发的权重和,问题就转化为如何求从某点出发,遍历某食材的权重和。由于图为一棵树,所有该权重和是唯一的。
有两个限制条件:如何知道某食材的全部酒店已经经过、每个点如何到达下一个点(即遍历的顺序/路径)。
再仔细思考问题,若从某点出发遍历树中的任意子集S并最后回到该点,将该点看作根节点,则其到每个节点所经过的路径(记作E)都要走两遍。
如果不回到该点,那么就可以选一个距离根节点最远的点减去,因为这条路径不需要走两遍。
发现这个关键结论后,就可以知道两个限制条件如何解决:直接将该点作为根节点遍历整棵树,用dp存储在E上的路径总和,转移方程为
dp[u] = \sigma_{i属于sons} (sons子树存在需求酒店或son本身为需求酒店)*dp[i] + w
这样就可以不用在意遍历的顺序。启示是,不能总用线性搜索的方式看待问题,深入考虑到树状问题的子结构性质,可以解决许多看起来毫不相关的问题。
#include <cstdio> #include <iostream> #include <algorithm> #include <vector> #include <set> #include <string.h> #define up(l,r,i) for(int i=l;i<=r;i++) #define dn(l,r,i) for(int i=r;i>=l;i--) typedef long long ll; #define int ll using namespace std; inline int _max(const int& a,const int& b){return a>b?a:b;} inline int _min(const int& a,const int& b){return a<b?a:b;} const int MAXN = 104,MAXM = 15; struct Node{ int to,w,nxt; Node():to(0),w(0),nxt(0){} }nd[MAXN<<1]; int head[MAXN],ecnt; int fa[MAXN],dp[MAXN]; void add(int a,int b,int w){ nd[++ecnt].to = b; nd[ecnt].w = w; nd[ecnt].nxt = head[a]; head[a] = ecnt; } int ned[MAXN][MAXM]; int n,m,k; int dfs(int u,int tp){ int mx = 0; for(int i = head[u]; i; i = nd[i].nxt){ int v = nd[i].to; int w = nd[i].w; if(fa[u] == v) continue; fa[v] = u; int t = dfs(v,tp); if(ned[v][tp] || dp[v]){ dp[u] += dp[v] + (w<<1); mx = _max(mx,t+w); } } return mx; } signed main() { //freopen("y.in","r",stdin); ios::sync_with_stdio(false); cin>>n>>m>>k; up(1,n,i){ up(1,k,j){ cin>>ned[i][j]; } } up(1,n-1,i){ int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,c); } //if(n == 6 && m == 1 && k == 2) {cout<<15<<endl;return 0;} int mx = 0; up(1,k,i){ int mi = 0x777777f; up(1,n,j){ memset(dp,0,sizeof(dp)); memset(fa,0,sizeof(fa)); int t = dfs(j,i); int ans = dp[j] - t; mi = _min(mi,ans); } //printf("对于类型%lld,计算结果为%lld\n",i,mi); mx = _max(mx,mi); } cout<<mx; return 0; }