[ABC341F] Breakdown
好一个 DP 套 DP!这里必须要锐评一下题面太抽象读题都花我 20 min
从一个点 删除一个棋子后,你可以选择与他直接相连的一些点放上棋子,不过要求被选择点权值的和小于 的权值 。显然,如果想要选择 的某个相邻点 ,一定有 (单个点的权值都超了肯定没法选)。凭借权值的关系,就能发现这张图实际是一张 DAG。
DAG 说明了无后效性,可以在图上跑 DP,单独计算某个点的最大贡献。又因为棋子之间不会互相干扰,因此只计算某点只有一个棋子时的最大贡献,最后统计时乘以当前点棋子数量即可。
如何统计某个点上有一个棋子时产生的贡献?我们发现:问题“限制选择的点的权值和不超过当前点,求总贡献最大”非常像一个 0-1 背包问题。计这个函数为 。假如我们正在求解 ,目前在考虑是否选择 。如果选择了 ,则 上会出现一枚棋子,产生 的贡献。因此, 看作物品体积, 看作物品价值,可以跑背包。移除当前点本身也可产生 的贡献,跑背包 dp 的数组记得初始化为 。
使用递归的形式求贡献 ,搭配记忆化食用效果更佳。递归时处理好 的情况,可以不用显式建 DAG。
#include<bits/extc++.h>
using namespace std;
namespace pbds=__gnu_pbds;
using ui=unsigned int;
using uli=unsigned long long int;
using li=long long int;
using usi=unsigned short int;
uli f(size_t p,vector<usi> const& w,vector<ui> const& a,vector<uli>& d,vector<vector<size_t>>& mp){
if (~d[p]) return d[p];
d[p]=0;
vector<uli> dp(w[p],1);
for (size_t i:mp[p])
for (usi j=w[p]-1;j>=w[i];--j) dp[j]=max(dp[j],dp[j-w[i]]+f(i,w,a,d,mp));
return d[p]=dp.back();
}
int main(void){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
size_t n,m;cin>>n>>m;
vector<vector<size_t>> mp(n);
while (m--){
size_t x,y;cin>>x>>y;--x,--y;
mp[x].push_back(y),mp[y].push_back(x);
}
vector<usi> w(n);vector<ui> a(n);
for (usi& i:w) cin>>i;
for (ui& i:a) cin>>i;
vector<uli> d(n,~0);
uli ans=0;
for (size_t i=0;i<n;++i) ans+=a[i]*f(i,w,a,d,mp);
cout<<ans;
return 0;
}
浙公网安备 33010602011771号