美团2024届秋招笔试第一场编程 【小美的树上染色】

题目描述:

小美拿到了一棵树,每个节点有一个权值。初始每个节点都是白色。

小美有若干次操作,每次操作可以选择两个相邻的节点,如果它们都是白色且权值的乘积是完全平方数,小美就可以把这两个节点同时染红。

小美想知道,自己最多可以染红多少个节点?

美团2024届秋招笔试第一场编程 【小美的树上染色】_第1张图片

美团2024届秋招笔试第一场编程 【小美的树上染色】_第2张图片

开始看到这个题的时候,已经下意识认定这是一道树形DP,状态定义大概是f[i][0],f[i][1],分别表示

i节点是白色情况下,i子树内包含的红节点最大数;i节点是红色情况下,i子树内包含的红节点最大数。f[i][0]的求法还是比较好求的:假设i的儿子是j,则f[i][0]+=max(f[j][0],f[j][1])。但求f[i][1]的时候好像卡住的,有点想法,不知道对不对。 

然后我把题给旁边一佬看了,结果令我没想到的是,他看到这个题,首先就是一种贪心(在输入的时候处理答案,对输入的俩个节点,如果它们权值相乘能凑成平方数就标记,也就是不建树)然后给我过了样例。当时听完之后,想了想【啊哈,这种题不建树是能写出来的吗,不过想了一些样例,好像确实是这样】。再想了几分钟之后,感觉不对啊,于是搞了个样例,它的解法是依赖输入的顺序的。【虽然他的解法有缺陷,但我发现我这固定思维是个很大缺点哪,一眼DP(虽然dp和贪心还是有很大关联),根本没往其它方向想】 这让我有了个认识【哪怕你AC了一道题,也要有看题解或者别人代码的必要,可以发散思维

之后结合他的想法,发现这个题确实可以不用dp,建树完之后从叶子节点一直往上贪心。

#include
#include
#include
using namespace std;

typedef long long LL;
const int N=1e5+10;
vectorg[N];
int n;
int st[N];
int ans;
LL w[N];


bool fun(LL x)
{
    LL d=sqrt(x);
    return d*d==x;
}
    
void dfs(int u,int fa)
{
    for(auto x:g[u]){
	   if(x==fa)continue;//保证不往回走,确保时间复杂度O(N)
	   dfs(x,u);
	   if(fun(w[u]*w[x])&&(!st[x]&&!st[u])){
	       ans+=2;
	       st[x]=1;
	       st[u]=1;
	   }
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>w[i];
    for(int i=0;i>u>>v;
	   g[u].push_back(v);
	   g[v].push_back(u);
    }
    dfs(1,-1);
    cout<

【很充实的一天,下午到晚上,听了一节3.5h的Linux和3h的C++,Linux敲了很多指令,

C++学到了缺省参数,函数重载,一点汇编,C++的函数名修饰规则(C为什么不支持函数重载的原因),引用...

加油!制定9_18到12_18的任务必须完成!

你可能感兴趣的:(刷算法题,算法)