CF437C The Child and Toy
CF437C The Child and Toy
题意翻译
n个带权点,m条无向边,删除一个点就要付出所有与之有联系且没有被删除的点的点权之和的代价。
求删除所有点的最小代价。
题解:
2020.11.26模拟赛T3 满分场。
场上看到最优化先想了DP。但是这道题压根就划分不出状态。看到很多大神用的拓扑序解法。各种玄学,反正我是没想到。既然DP没戏了,那就开始想贪心。
怎么贪呢?手推了几组样例,发现每次删除的总是当前没被删除的所有点中权值最大的那个点。然后按这个顺序都删完就是最优解。(出题人向其中加入了重边和自环,所以代码里有些许这道题用不上的点)
考场上没证明这个贪心,过了大样例就没再管。
然后场下证明这个贪心:如果不是每次删除当前状态下权值最大的节点,那么会导致这个节点至少被算入贡献一次。此时就没有直接删除它更优秀。
代码:
#include<cstdio>
#include<set>
#include<queue>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=1e4+4;
int n,m,ans;
int w[maxn];
bool v[maxn];
priority_queue<pair<int,int> >q;
set<int> e[maxn];
int tot,head[maxn],nxt[maxn<<2],to[maxn<<2];
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
signed main()
{
// freopen("star.in","r",stdin);
// freopen("star.out","w",stdout);
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&w[i]);
q.push(make_pair(w[i],i));
}
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%lld%lld",&x,&y);
if(x==y)
continue;
if(e[x].find(y)==e[x].end())
{
add(x,y);
add(y,x);
e[x].insert(y);
e[y].insert(x);
}
}
while(!q.empty())
{
int x=q.top().second;
q.pop();
v[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(v[y])
continue;
ans+=w[y];
}
}
printf("%lld\n",ans);
return 0;
}