仍然是一道标准的树形DP。
我们其实不必纠结到底剩余的钱数是多少,那并不重要,我们只关心其是否大于0。而且我们其实要求的是能连接的最大用户数。
用dp[i][j]表示在第i个节点连接了j个客户的最大利润。那么得到:
dp[i][j] = max(dp[i][j],dp[i][j-k] + dp[t][k] - e[i].v);
之后又可以做了……(好像这样的树形DP推出方程即结束)
注意初值要设成-INF(因为有负数)。还是因为背包的思想,我们在枚举第一维(当前点连接顾客数)的时候要倒着枚举。
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; typedef long long ll; const int M = 10005; const int INF = 1000000009; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct edge { int next,to,v; }e[10005]; int a[10005],n,m,dp[3005][3005],x,y,ecnt,head[10005]; void add(int x,int y,int z) { e[++ecnt].to = y; e[ecnt].v = z; e[ecnt].next = head[x]; head[x] = ecnt; } int dfs(int x,int fa) { dp[x][0] = 0; if(x > n-m) { dp[x][1] = a[x]; return 1; } int tot = 0; for(int i = head[x];i;i = e[i].next) { int t = e[i].to; if(t == fa) continue; int gg = dfs(t,x); tot += gg; per(j,tot,1) { rep(k,0,gg) dp[x][j] = max(dp[x][j],dp[t][k] + dp[x][j-k] - e[i].v); } } return tot; } int main() { n = read(),m = read(); rep(i,1,n) rep(j,1,n) dp[i][j] = -INF; rep(i,1,n-m) { int g = read(); rep(j,1,g) x = read(),y = read(),add(i,x,y),add(x,i,y); } rep(i,n-m+1,n) a[i] = read(); dfs(1,0); per(i,m,0) { if(dp[1][i] >= 0) { printf("%d ",i); break; } } return 0; }