由于路径比节点少,说明没有环,可以认为是树

其实就是树(图)的根必须选版最大独立集问题。。。

因为对图的储存和遍历不是很熟悉,就把三个方法都实现了一遍。

这题真的是medium吗,最大独立集是经典问题没错,可是性能限制真的好严格。

用拉链表连递归和std栈都不敢用,手写循环和栈勉强通过,用链式前向星才能稳过,怎么看也是hard吧!

临接矩阵

通过率9/20,测了下数据量到五万,在创建数组那一步如果用原生数组会栈溢出,用vector会内存超限。除非是很密集的图(比如边数接近n平方的有向图)基本上可以放弃这个方法了。

#include <any>
#include <climits>
#include <iostream>
#include <deque>
#include <vector>
using namespace std;
 
int main() {
    int n,cur,first,t1,t2,i,j,top,root;
    cin>>n>>first;
    first--;//转下标
    //cout<<n<<' '<<cur<<endl;
    bool neighbor[n][n];
    for(i=0;i<n;++i)for(j=0;j<n;++j)neighbor[i][j]=false;
    int dp[n][2];
    //由于路径比节点少,说明没有环,可以认为是树
    //由于是无向的,亲子不分,所以只能记录已经走过的节点
    bool visited[n];
    for(i=0;i<n;++i)visited[i]=false;
    int post_order[n+1],st[n];//本来应该用栈,n已知,可以用数组预分配内存
 
    for(i=1;i<n;++i){
        //n-1条路径
        cin>>t1>>t2;
        t1--;t2--;
        neighbor[t1][t2]=true;
        neighbor[t2][t1]=true;
    }
 
  // for(i=0;i<n;++i)for(j=0;j<n;++j)cout<<neighbor[i][j];
    //构建后续遍历
     i=n-1;top=0; st[0]=first;
 
     while(top>=0){
        cur=st[top];
        --top;
        visited[cur]=true;
        post_order[i]=cur;
        --i;
        //cout<<cur<<endl;
        for(j=0;j<n;++j){
            if(visited[j]){continue;}
            if(neighbor[cur][j]){
                top++;
                st[top]=j;
            }
        }
    }
 
    for(i=0;i<n;++i)visited[i]=false;
    post_order[n]=INT_MAX;
    for(i=0;i<n;++i){
        cur=post_order[i];
        //cout<<cur<<endl;
        dp[cur][1]=1;
        dp[cur][0]=0;
        for(j=0;j<n;++j){
            if(!visited[j]){
                //cout<<neighbor[cur][j]<<"未定义"<<endl;
                continue;}
            if(neighbor[cur][j]){
                 dp[cur][1]+=dp[j][0];
                    dp[cur][0]+=max(dp[j][0],dp[j][1]);//注意这里,未选情况下子可选可不选,不要被惯性思维误导选了
            }
           
        }
       // cout<<dp[cur][0]<<' '<<dp[cur][1]<<endl;
        visited[cur]=true;
        //cout<<cur<<' '<<dp[cur][0]<<' '<< dp[cur][1]<<endl;
       
    }
    cout<<dp[first][1]<<endl;
   
 
}
// 64 位输出请用 printf("%lld")

邻接表(拉链表)

要创建n个动态数组,内存使用效率略逊于链式前向星。

最复杂用例性能

最简单用例性能

stack容器性能很差,既然最大长度已知,这里用数组实现了栈的功能,如果用std的栈用例通不过。

遍历用的循环法,如果用递归估计也通不过用例(悲)

很极限地全ac了,一共一百个人刷题排九十多。

#include <any>
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
 
int main() {
    int n,cur,first,t1,t2,i,j,top,root;
    cin>>n>>first;
    first--;//转下标
    //cout<<n<<' '<<cur<<endl;
    vector<int> neighbor[n];
    int dp[n][2];
    //由于是无向的,亲子不分,所以只能记录已经走过的节点
    bool visited[n];
    for(i=0;i<n;++i)visited[i]=false;
    int post_order[n+1],st[n];//本来应该用栈,n已知,可以用数组预分配内存
 
    for(i=1;i<n;++i){
        //n-1条路径
        cin>>t1>>t2;
        t1--;t2--;
        neighbor[t1].push_back(t2);
        neighbor[t2].push_back(t1);
    }
 
 
    //构建后续遍历,两种方法,都需要多出一个数组储存
     i=n-1;top=0; st[0]=first;
 
     while(top>=0){
        cur=st[top];
        --top;
        visited[cur]=true;
        post_order[i]=cur;
        --i;
        for(j=0;j<neighbor[cur].size();++j){
            if(visited[neighbor[cur][j]])continue;
            top++;
            st[top]=neighbor[cur][j];
        }
    }
 
    for(i=0;i<n;++i)visited[i]=false;
    post_order[n]=INT_MAX;
    for(i=0;i<n;++i){
        cur=post_order[i];
        dp[cur][1]=1;
        dp[cur][0]=0;
        for(j=0;j<neighbor[post_order[i]].size();++j){
            if(!visited[neighbor[cur][j]]){
                //cout<<neighbor[cur][j]<<"未定义"<<endl;
                continue;}
            dp[cur][1]+=dp[neighbor[cur][j]][0];
            dp[cur][0]+=max(dp[neighbor[cur][j]][0],dp[neighbor[cur][j]][1]);//注意这里,未选情况下子可选可不选,不要被惯性思维误导选了
             
        }
        visited[cur]=true;
        //cout<<cur<<' '<<dp[cur][0]<<' '<< dp[cur][1]<<endl;
       
    }
    cout<<dp[first][1]<<endl;//出发点一定要选
   
 
}
// 64 位输出请用 printf("%lld")

链式前向星

我再研究下循环法怎么写(。)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e6+1000;
int n,s,f[N][2];
int ver[N],nxt[N],head[N],tot;
inline void add(int x,int y)
{
    ver[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
inline void dfs(int x,int fa)
{
    f[x][1]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==fa)continue;
        dfs(y,x);
        f[x][1]+=f[y][0];
        f[x][0]+=max(f[y][0],f[y][1]);
    }
}
int main()
{
    cin>>n>>s;
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(s,0);
   cout<<f[s][1]<<endl;
    return 0;
}