bzoj4027: [HEOI2015]兔子与樱花 贪心
本人总觉得贪心不清真不是正解,可有的骚题他就是正解而且我还老碰上,所以说,有的时候是要感性理解一下或者证一下,贪心大佬.....
这个题看数据,dp太难,往贪心那边想一下吧.....
本题的做法是建树,从叶结点开始合并代价直到不行为止(证明:首先假设我们已经找到一个最优解但他不是从叶到根合并到死,那么对于合并的点我们可以把其中的点从低到高依次再合并他的子节点,第一层由于合并了子节点而他又是最优解所以最多合并一个子节点并把它自己释放,第二层同理....最高层因为是最优解肯定不能再合并子节点;队友不适合冰点的点肯定不能再合并,所以我们叶到根每个点都是合并到不能合并,证毕),在合并之后只要把他的代价加上就好,因为他的儿子不可能再合并到他的父亲所以他的父亲只能合并它和他的兄弟,所以每个节点只可能去合并直系儿子。
证明好累.......
这个儿子当然要从代价小到代价大合并,所以邻接表和邻接矩阵都是不好实现的,因为要动态,所以vector最方便....
考试的时候总觉得贪心不清真,就随便大了几个贪心,就挂零了....
#include<cstdio> #include<vector> #include<algorithm> #define MAXN 2000005 using namespace std; vector<int>e[MAXN]; int c[MAXN],n,son[MAXN],ans,m; int comp(const int a,const int b) { return c[a]<c[b]; } void dfs(int x) { for(int i=0;i<e[x].size();i++) dfs(e[x][i]); sort(e[x].begin(),e[x].end(),comp); c[x]+=e[x].size(); for(int i=0;i<e[x].size();i++) { if(c[x]+c[e[x][i]]-1>m)break; ans++; c[x]+=c[e[x][i]]-1; } } int main() { //freopen("sakura.in","r",stdin); //freopen("sakura.out","w",stdout); scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%d",&c[i]); for(int i=0;i<n;i++) { scanf("%d",&son[i]); for(int j=1;j<=son[i];j++) { int x; scanf("%d",&x); e[i].push_back(x); } } dfs(0); printf("%d",ans); return 0; }
苟利国家生死以, 岂因祸福避趋之。