2019 ACM ICPC Xi'an University of Posts & Telecommunications School Contest(i,j题题解)

    xiaoxiao2023-11-25  169

    I 题题解 题意 给你一颗树 树上每个点涂了颜色 总共只有两种颜色 问你不同颜色点直接的距离之和为多少

    思路 :树形dp问题 考虑每条边的贡献度 这条边的左边的结点和右边节点染色不同的点数目相乘在乘以权值就是条边的贡献度 这样说比较抽象 看下面dp数组含义比较好理解

    设num[0] 为这颗树的染色为0的节点总数 num[1] 为这颗树染色为1的节点的总数

    dp[i][0] 表示当前的节点子和其子节点染色为0的个数 dp[i][1] 表示当前节点和其子节点染色为1的个数 那么连接着这个节点的这条边的另一端染色为1的节点数目就为 num[1] - dp[i][1] 染色为0的节点个数就为 num[0 ]- dp[i][0] 设这条边的权值为w 那么这条边的贡献就为

    w*( num[0] - dp[i][0]) * dp[i][1] + w* (num[1] - dp[i][1]) * dp[i][0] dfs算一下每条边的贡献度就好了

    #include<bits/stdc++.h>// 考虑计算每个边的贡献度 我这个边能过被计算几次 那么就等于我左边的1的个数乘以右边0的个数 using namespace std; const int maxn = 1e5+10; const int mod = 998244353; typedef pair<int,int> p; typedef long long ll; ll dp[maxn][2],a[maxn],ans = 0,num[2]; vector<p> g[maxn]; void dfs(int son,int fa) { for(int i = 0; i < g[son].size(); i++) { int w = g[son][i].second,u = g[son][i].first; if(u==fa) continue; dfs(u,son);// 从最底层开始回溯 ans += w*((num[0] - dp[u][0])*dp[u][1])%mod + w*((num[1] - dp[u][1])*dp[u][0])%mod;// 计算贡献度 ans %= mod; dp[son][0] += dp[u][0];// 更新节点信息 dp[son][1] += dp[u][1]; } dp[son][0] += a[son]==0;//我为自己代言!! 要算上自己这个点的贡献度 dp[son][1] += a[son]==1; } int main() { int n,u,v,w;// cin>>n; for(int i = 1; i <= n; i++) { cin>>a[i]; num[a[i]]++; } for(int i = 0; i < n-1; i++) { cin>>u>>v>>w; g[u].push_back(p(v,w)); g[v].push_back(p(u,w)); } dfs(1,-1); cout<<(ans*2)%mod<<endl; }

    j题题意 自己应该懂吧 这里就不多说了 思路: 典型的01背包问题 就是有几个状态的转移过来 使用碎片的话那么就是消耗1体积 也就是 p - 1 设dp[i][j][k] 表示钱为i 积分为j 碎片为 k 是的最大值 那么这个就有三个转移状态 对于每个 物品 有 if(j>= w[i]) dp[i][j[k] = max(dp[i][j][k],dp[i][j - w[i] ][k] + val[i]);// 能转移 从钱转移过来 if(i >= v[i]) dp[i[j][k] = max(dp[i][j][k],dp[i - v[i]][j][k] + val[i]);// 能转移 从积分转移过来 if(k > 0) dp[i][j][k] = max(dp[i][j[k],dp[i][j][k-1] + val[i]);// 能转移 从碎片转移过来

    类似的使用01背包 三个状态每次从最大值开始滚动一下 防止多算了一次 就可以算出答案了 代码如下

    #include<bits/stdc++.h> using namespace std; const int maxn = 110; int v[maxn],w[maxn],val[maxn]; int dp[maxn][maxn][6]; int main() { int n,m,w1,k,ans = 0; while(cin>>n>>m>>w1>>k) { ans = 0; memset(dp,0,sizeof(dp)); memset(v,0,sizeof(v)); memset(w,0,sizeof(w)); memset(val,0,sizeof(val)); for(int i = 0; i < n; i++) { cin>>v[i]>>w[i]>>val[i]; } for(int i = 0; i < n; i++) { for(int j = m; j >= 0; j--) { for(int l = w1; l >= 0; l--) { for(int p = k; p >= 0; p--)能过转移 然后就可以转移的 { if(l >= w[i]) dp[j][l][p] = max(dp[j][l][p],dp[j][l - w[i] ][p] + val[i]); if(j >= v[i]) dp[j][l][p] = max(dp[j][l][p],dp[j - v[i]][l][p] + val[i]); if(p > 0) dp[j][l][p] = max(dp[j][l][p],dp[j][l][p-1] + val[i]); } } } } cout<<dp[m][w1][k]<<endl; } return 0; }
    最新回复(0)