AtCoder Beginner Contest 341-F
AtCoder Beginner Contest 341-F
Problem
给你一个由 个顶点和 条边组成的简单无向图。每个顶点拥有权重,并且被放置了个棋子。
只要图形上还有棋子,就重复下面的操作:
- 首先,从图形中选择一个(有棋子的)顶点并移除一个棋子。
- 从相邻点中选择出一些点组成集合(可以不选),要保证这个集合内的所有点的权重之和小于顶点,即,并在中的每个顶点上放置一个棋子。
请求出最多最多能进行多少次这样的操作。
可以证明,无论如何操作,在有限次迭代后,图形上将没有棋子。
Constraints
Solution
首先再此解释一下题目中的操作:
假设现在图是这样的:
(为了方便,图中的数字既表示顶点,同时也表示该点的权重)
其中顶点5上有一颗棋子,并且现在选择,开始操作。
首先取下5上的棋子,接下来选择5的相邻点的一个集合(比如1,3
),且保证集合内点的权重之和小于。因此我们可以选1,3
,可以选1
,可以选3
,也可以不选,但是不能选6
。
此时可以发现,我们始终只能选择比点要小的点,也就是说棋子的扩散方向永远是单向的(3永远不可能给5棋子,5也永远不能给6棋子)
所以题目中说的简单无向图其实是个幌子,这其实是一个DAG
那么我们先将原图化为DAG,再按照权重升序来看各个顶点(小权重顶点不会对大权重顶点有干扰,棋子之间也不会有相互作用),进行DAG上的dp。
具体的,设X[i]
表示若顶点i
有一枚棋子,可以操作的次数
当轮到点x
的时候,权重比其小的出点都已经计算好了X[i]
,现在需要抉择出如何选择他的出点集合,使得在权重之和不超过的情况下,尽力包含更多的X[i]
——相当于做个背包问题了。
Code
#define N 5020
int n,m;
vector<pair<int,int>>edge;
int W[N],A[N];
pair<int,int>ww[N];
vector<int>e[N];
int order[N];
bool cmp(int x,int y)
{
return W[x]<W[y];
}
LL dp[N];
LL X[N];
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int x,y;
cin>>x>>y;
edge.push_back(make_pair(x,y));
}
for(int i=1;i<=n;i++)
{
cin>>W[i];
}
for(int i=1;i<=n;i++)
{
cin>>A[i];
}
for(int i=0;i<m;i++)
{
if(W[edge[i].first]>W[edge[i].second]) e[edge[i].first].push_back(edge[i].second);
if(W[edge[i].first]<W[edge[i].second]) e[edge[i].second].push_back(edge[i].first);
}
for(int i=1;i<=n;i++) order[i]=i;
sort(order+1,order+n+1,cmp);
for(int i=1;i<=n;i++) X[i]=1;
for(int i=1,x;i<=n;i++)
{
x=order[i];
memset(dp,0,sizeof(dp));
dp[0]=1;
for(unsigned int j=0;j<e[x].size();j++)
{
int y=e[x][j];
for(int k=W[x]-1;k-W[y]>=0;k--)
{
dp[k]=max(dp[k],dp[k-W[y]]+X[y]);
}
}
for(int k=0;k<=5000;k++) X[x]=max(X[x],dp[k]);
}
LL ans=0;
for(int i=1;i<=n;i++) ans+=X[i]*A[i];
cout<<ans;
return 0;
}
Attention
记得开long long
注意背包dp[i]
与X[i]
的初值
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】