图上的游戏,51nod1481,bitset优化遍历

博客探讨将独立集合并成链的问题。先枚举链端点,发现存在端点独立集仅一个点的最优方案。接着分析点的连接情况判断是否有解,原过程复杂度为O(n(n+m))。最后提出先判断奇环,再用bitset优化图遍历,使时间复杂度变为O(m+64n3)。

正题

大概想了一个晚上吧,首先可以发现就是将一个独立集不断合并起来之后,成为一个链的形状,相当于有边的只有相邻的两个独立集。
考虑枚举链的一个端点,容易发现存在一种最优方案满足这个端点的独立集只有一个点,否则可以将同一个独立集的其他点放到链上第三个独立集,不影响后面的顺序而且只会使答案变得更大。
枚举完这个点之后,考虑当前层的点的连接点有没有被加入到独立集,如果没有,那么就加入到下一层,否则,如果和当前点在同一层(也就是有奇数环),那么无解,否则就不用理(肯定会在当前点的上一层或者下一层),肯定合法。
这个过程是O(n(n+m))O(n(n+m))O(n(n+m))的。
考虑先判断是否有奇环,后面再使用bitset优化图的遍历就可以了,时间复杂度O(m+n364)O(m+\frac{n^3}{64})O(m+64n3)

#include<bits/stdc++.h>
using namespace std;

const int N=1010;
int first[N],len,n,m;
int op[N],qs[N];
bool tag[N],vis[N];
vector<int> V;
bitset<1001> s[1001],f,g,S;

bool dfs(int x,bool c){
	vis[x]=true;tag[x]=c;V.push_back(x);S[x]=true;
	bool tf=true;
	int now=s[x]._Find_next(0);
	while(now<=n){
		if(!vis[now]) tf&=dfs(now,c^1);
		else if(tag[x]==tag[now]) return false;
		now=s[x]._Find_next(now);
	}
	return tf;
}

int gs(int x){
	g.reset();g[x]=true;
	vector<int> P;
	int ans=-1;
	while(1){
		if(g.count()==0) break;
		ans++;P.resize(0);
		int now=g._Find_next(0);
		while(now<=n) P.push_back(now),now=g._Find_next(now);
		g.reset();for(int i=0;i<P.size();i++) g|=s[P[i]],f[P[i]]=0;g&=f;
	}
	return ans;
}

int solve(int x){
	V.resize(0);S.reset();
	if(!dfs(x,0)) return -1;
	int mmax=0;
	for(int i=0;i<V.size();i++) f=S,mmax=max(mmax,gs(V[i]));
	return mmax;
}

int main(){
	scanf("%d %d",&n,&m);
	int x,y;
	for(int i=1;i<=m;i++) scanf("%d %d",&x,&y),s[x][y]=s[y][x]=true;
	int ans=0;
	for(int i=1;i<=n;i++) if(!vis[i]){
		int tmp=solve(i);
		if(tmp==-1) {printf("-1");return 0;}
		ans+=tmp;
	}
	printf("%d",ans);
}
### 关于51Nod 3100 上台阶问题的C++解法 #### 题目解析 该题目通常涉及斐波那契数列的应用。假设每次可以走一步或者两步,那么到达第 \( n \) 层台阶的方法总数等于到达第 \( n-1 \) 层和第 \( n-2 \) 层方法数之和。 此逻辑可以通过动态规划来解决,并且为了防止数值过大,需要对结果取模操作(如 \( \% 100003 \)[^1])。以下是基于上述思路的一个高效实现: ```cpp #include <iostream> using namespace std; const int MOD = 100003; long long f[100010]; int main() { int n; cin >> n; // 初始化前两项 f[0] = 1; // 到达第0层有1种方式(不移动) f[1] = 1; // 到达第1层只有1种方式 // 动态规划计算f[i] for (int i = 2; i <= n; ++i) { f[i] = (f[i - 1] + f[i - 2]) % MOD; } cout << f[n] << endl; return 0; } ``` 以上代码通过数组 `f` 存储每层台阶的结果,利用循环逐步填充至目标层数 \( n \),并最终输出结果。 --- #### 时间复杂度分析 由于仅需一次线性遍历即可完成所有状态转移,时间复杂度为 \( O(n) \)。空间复杂度同样为 \( O(n) \),但如果优化存储,则可进一步降低到 \( O(1) \): ```cpp #include <iostream> using namespace std; const int MOD = 100003; int main() { int n; cin >> n; long long prev2 = 1, prev1 = 1, current; if (n == 0 || n == 1) { cout << 1 << endl; return 0; } for (int i = 2; i <= n; ++i) { current = (prev1 + prev2) % MOD; prev2 = prev1; prev1 = current; } cout << prev1 << endl; return 0; } ``` 在此版本中,只保留最近两个状态变量 (`prev1`, `prev2`) 来更新当前值,从而节省内存开销。 --- #### 输入输出说明 输入部分接受单个整数 \( n \),表示台阶数量;程序会返回从地面走到第 \( n \) 层的不同路径数目,结果经过指定模运算处理以适应大范围数据需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值