卧薪尝胆,厚积薄发。
JSOI2015 salesman
Date: Wed Sep 26 19:18:39 CST 2018 In Category: NoCategory

Description:

给一棵树,每个节点有限制经过次数,到每个点时会获得权值,最大化权值和,并判断解是否唯一。
$1\leqslant n \leqslant 10^5$

Solution:

树形贪心,每个点限制经过 $k$ 次,也就是说只能访问 $k-1$ 个子树,于是把所有子树的值拿出来排序,取最大的为正的 $k-1$ 个即可,至于解不为一的情况,有三种可能:有值为 $0$ 的子树,有子树解不为一和最后一个选的和下一个权值相同。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n;
#define MAXN 100010
struct edge
{
int to,nxt;
}e[MAXN << 1];
int edgenum = 0;
int lin[MAXN] = {0};
void add(int a,int b)
{
++edgenum;e[edgenum].to = b;e[edgenum].nxt = lin[a];lin[a] = edgenum;
++edgenum;e[edgenum].to = a;e[edgenum].nxt = lin[b];lin[b] = edgenum;
return;
}
int val[MAXN],lim[MAXN];
bool vis[MAXN];
int f[MAXN];
bool g[MAXN];
int s[MAXN],tp = 0;
bool cmp(int a,int b){return a > b;}
void dp(int k)
{
vis[k] = true;
g[k] = false;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(!vis[e[i].to])
{
dp(e[i].to);
g[k] |= g[e[i].to];
}
}
tp = 0;
for(int i = lin[k];i != 0;i = e[i].nxt)
if(!vis[e[i].to])
s[++tp] = f[e[i].to];
sort(s + 1,s + 1 + tp,cmp);
f[k] = 0;
for(int i = 1;i < lim[k] && i <= tp && s[i] >= 0;++i)
{
if(s[i] == 0)g[k] = true;
f[k] += s[i];
}
f[k] += val[k];
if(tp >= lim[k] && lim[k] != 2 && s[lim[k] - 1] == s[lim[k] - 2])g[k] = true;
vis[k] = false;
return;
}
int main()
{
scanf("%d",&n);
for(int i = 2;i <= n;++i)scanf("%d",&val[i]);
for(int i = 2;i <= n;++i)scanf("%d",&lim[i]);
lim[1] = 0x3f3f3f3f;
int a,b;
for(int i = 1;i < n;++i)
{
scanf("%d%d",&a,&b);
add(a,b);
}
dp(1);
cout << f[1] << endl;
if(g[1])puts("solution is not unique");
else puts("solution is unique");
return 0;
}
In tag: 算法-贪心
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡