由于路径比节点少,说明没有环,可以认为是树
其实就是树(图)的根必须选版最大独立集问题。。。
因为对图的储存和遍历不是很熟悉,就把三个方法都实现了一遍。
这题真的是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;
}

京公网安备 11010502036488号