Educational Codeforces Round 49 (Rated for Div. 2) (D/dfs找环+E/线性dp+F/并查集)

本文精选三道算法竞赛题目,包括最小捕鼠夹放置成本、满足特定条件的染色方案数以及考试时间安排问题的解决方案。通过对每个问题的深入解析,提供详细的思路和代码实现,适合算法爱好者和竞赛选手学习参考。

思路来源

https://codeforces.com/blog/entry/61311(官方题解)

https://www.cnblogs.com/tobyw/p/9513876.html(E题)

D.Mouse Hunt

n(n<=2e5)个房间,有一只老鼠,可能在t=0时出现在任意房间,

第i个房间放捕鼠夹的代价是ci(1<=ci<=1e4),

在这一秒出现在i房间的老鼠下一秒出现的房间号是ai(1<=ai<=n)

问最小的放捕鼠夹代价和,使得老鼠出现在任意一个房间都能被捕

题解

从一个点出发,要么到下一个点,要么到自身

所以每个连通块要么是一个ρ字形,要么是一个环(含自环)

考虑到,环上必须放捕鼠夹,且只放在环上就能保证该连通块可捕鼠,

那么放在环上的最小cost房间上即可,

dfs统计每个连通块的环,将每个环上的最小值计入贡献

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
int n,a[maxn+2],c[maxn+6];
int cnt;//cnt个cycle 
vector<int>cycle[maxn+5];
int vis[maxn+3];//为0未访问 为1在栈中 为2已访问 
int path[maxn+4],tot,tmp;
int mn,len,v;
ll ans;
void dfs(int u)
{
	path[++tot]=u;//记录当前路径,入栈 
	vis[u]=1;
	int v=a[u];//实际不用建图,找后继即可 
	if(vis[v]!=2)
	{
		if(vis[v]==1)//发现环 
		{
			cnt++;
			tmp=tot;//只是记录 并不在记录环时退栈 
			while(path[tmp]!=v)cycle[cnt].push_back(path[tmp--]);//退栈记录环 其实就是tarjan 
			cycle[cnt].push_back(v);
		}
		else dfs(v);
	} 
	--tot;//退栈 
	vis[u]=2;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	scanf("%d",&c[i]);
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]);
	for(int i=1;i<=n;++i) 
	if(!vis[i])dfs(i);
	for(int i=1;i<=cnt;++i)
	{
		len=cycle[i].size();
		mn=c[cycle[i][0]];//记录环上点pos 对应权值c[pos] 
		for(int j=0;j<len;++j)
		{
			v=cycle[i][j]; 
			mn=min(mn,c[v]);
		}
		ans+=mn; 
	}
	printf("%I64d\n",ans);
	return 0;
}

E.Inverse Coloring

n*n(1<=n<=500)的方格板,每个方格可以涂黑或白色,

求满足下列条件的方案数:

①相邻行的颜色完全相同或完全相反

②相邻列的颜色完全相同或完全相反

③单一颜色的联通块长方形面积小于给定的k(1<=k<=n*n)

题解

用01序列来代表行的染色情况,那么有01对应黑白①和01对应白黑②两种,

如果行的01序列、列的01序列确定,就可以按数独一样,确定整张图

再在染色方法①、②中选一种,唯一确定这张图

把01序列转化成连续的计数,如011000转化为123,

那么根据坐标离散化,新的点的面积=离散化的行宽*离散化的列高,

要求每个矩形的面积都小于k,即最大值小于k,即行宽最大值*列高最大值<k(因为x行y列一定会有一个交叉点(x,y))

考虑和为n,最大值为m的方案数dp[n][m]:

该方案数可以由其子情况补上一个不大于m的数得来,可以补1...2...m

即dp[n][m]=dp[n-1][m]+(dp[n-2][m]+...+dp[n-m][m])③

类比有dp[n-1][m]=(dp[n-2][m]+...+dp[n-m][m])+dp[n-m-1][m]④

④代③有,dp[n][m]=2*dp[n-1][m]-dp[n-m-1][m]

前提是n-m-1>=0,即n>=m+1,这也意味着dp[0][m]得赋值为1,作为base情况

而n<=m的情况,意味着最大值大于等于n,即整数n任意划分

n个球n-1个空任意放挡板的方案数2^{n-1}

dp[n][m]=2*dp[n-1][m]-dp[n-m-1][m](n>m)

dp[n][m]=2^{n-1} (n<=m)

注意到最大值具有前缀和的性质,即行max为m,是这一行的max不超过m,

统计这一行max恰为m的方案数,用max[m]-max[m-1]即可

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=500;
const int N=maxn+5;
//dp[i][j]表示和为i最大值为j的方案数 
int n,k,dp[N][N];
int ans,now;
void add(int &x,int y)
{
	x+=y;
	if(x<0)x+=mod;
	if(x>=mod)x-=mod;
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=maxn;++i)
	dp[0][i]=1;//底部条件 便于n-m-1==0 
	dp[1][1]=1;//dp[n][n]=2^(n-1) n个任取 
	for(int i=2;i<=maxn;++i)
	dp[i][i]=(dp[i-1][i-1]*2)%mod;
	for(int i=1;i<=maxn;++i)
	{
		for(int j=1;j<i;++j)
		dp[i][j]=(2*dp[i-1][j]%mod-dp[i-j-1][j]%mod+mod)%mod;
		for(int j=i+1;j<=maxn;++j)
		dp[i][j]=dp[i][i]; 
	}
	for(int i=1;i<=n;++i)//枚举行max 由行max*列max<=k-1可得 
	{
		now=dp[n][i]-dp[n][i-1];
		now=1ll*now*dp[n][min((k-1)/i,n)]%mod;
		add(ans,now);
	}
	printf("%d\n",(2*ans)%mod); 
	return 0;
} 

F.Session in BSU

n(1<=n<=1e6)门考试,第i门考试有第一次考试时间第ai天和第二次考试时间第bi天(1<=ai<bi<=1e9)

考试可能存在冲突的天,问是否能通过所有门的考试,

若能,输出通过考试的最小天数;若不能输出-1

题解

瞎搞并查集搞过去了

离散化ai和bi到2e6的范围,建图连ai和bi,

考虑每个连通块,每条边都得选一个点作为归属,边是考试,点是第几天

对于孤立点,没有边,直接跳过(事实上由于离散化,不会有这样的点)

对于树,点比边多一个,可以弃掉最大的点,取次大值

对于基环树,点边相等,不可舍弃,取最大值

对于有大于等于两个环的连通块,不能满足所有边都被选的要求

并查集维护每个连通块的最大值、次大值、环的个数,统计每个块对答案贡献

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
struct node
{
	int cyc,mx,se,fa,sz;
	node():cyc(0){
	}
}par[2*maxn];
int find(int x)
{
	return par[x].fa==x?x:par[x].fa=find(par[x].fa);
}
int n,a[maxn],b[maxn];
int c[2*maxn],cnt;
int tmp[4],tot;
int x,y,ans;
bool flag;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d%d",&a[i],&b[i]);
		c[++cnt]=a[i];
		c[++cnt]=b[i];
	}
	sort(c+1,c+cnt+1);
	cnt=unique(c+1,c+cnt+1)-(c+1);
	for(int i=1;i<=n;++i)
	{
		a[i]=lower_bound(c+1,c+cnt+1,a[i])-c;
		b[i]=lower_bound(c+1,c+cnt+1,b[i])-c;
		//printf("a[]:%d b[]:%d\n",a[i],b[i]);
	}
	for(int i=1;i<2*maxn;++i)
	{
		par[i].sz=1;
		par[i].fa=i;
		par[i].mx=i;
		par[i].se=-1;//不存在第二大 
		par[i].cyc=0;
	}
	for(int i=1;i<=n;++i)
	{
		x=find(a[i]),y=find(b[i]);
		if(x==y)
		{
			par[x].cyc++;
			//printf("time[%d]:cyc[%d]:%d\n",i,x,par[x].cyc);
			continue;
		}
		if(par[x].sz<par[y].sz)swap(x,y);
		tot=0;
		tmp[tot++]=par[x].mx;tmp[tot++]=par[x].se;
		tmp[tot++]=par[y].mx;tmp[tot++]=par[y].se;
		sort(tmp,tmp+tot);
		par[x].mx=tmp[3];
		par[x].se=tmp[2];
		par[x].sz+=par[y].sz;
		par[x].cyc+=par[y].cyc;
		par[y].fa=x;
	}
	for(int i=1;i<2*maxn;++i)
	{
		if(par[i].fa==i&&par[i].sz>1)
		{
			//printf("%d:%d %d %d %d\n",i,par[i].sz,par[i].mx,par[i].se,par[i].cyc);
			if(par[i].cyc>1)flag=1;
			else if(par[i].cyc==1)ans=max(ans,c[par[i].mx]);
			else ans=max(ans,c[par[i].se]);
		}
	}
	if(!flag)printf("%d\n",ans);
	else puts("-1");
	return 0;
}

 

内容概要:本文提出了一种基于非合作博弈理论的居民负荷分层调度模型,并结合双层鲸鱼优化算法(Two-level Whale Optimization Algorithm)进行高效求解,模型与算法均通过Matlab代码实现。研究针对电力系统中居民侧用电负荷的复杂调度问题,引入非合作博弈机制刻画各用户之间的利益竞争关系,实现负荷的分层优化分配;同时设计双层优化架构,上层优化资源配置,下层模拟用户自主决策行为,提升了模型的实用性与合理性。通过智能优化算法求解多层级、非凸非线性的博弈模型,有效提高了调度方案的收敛性与全局寻优能力,适用于现代智能电网中的需求侧管理与能源优化场景。; 适合人群:具备电力系统基础理论知识Matlab编程能力,从事智能电网、能源优化调度、需求侧管理、博弈论应用等方向的科研人员、高校研究生及工程技术人员。; 使用场景及目标:①应用于居民区电力负荷的分层优化调度系统设计与仿真分析;②为非合作博弈在多主体能源系统建模中的应用提供方法论支持;③利用双层鲸鱼算法解决具有嵌套结构的复杂双层优化问题,提升求解效率与调度方案的可行性。; 阅读建议:建议读者结合提供的Matlab代码深入理解模型构建逻辑与算法实现流程,重点关注博弈模型的效用函数设计、纳什均衡求解思路以及双层优化结构的迭代机制,宜配合实际用电数据开展复现实验以验证模型有效性与鲁棒性。
内容概要:本文围绕基于自适应神经模糊推理系统(ANFIS)智能控制器的可再生能源微电网功率管理系统展开研究,结合Simulink仿真实现,深入探讨了微电网中功率的智能调控与经济机组组合调度问题。通过引入ANFIS控制器,有效应对风能、光伏等可再生能源出力的波动性与不确定性,提升系统运行的稳定性与电能质量。研究内容涵盖微电网多源协调控制策略、功率平衡管理、优化调度模型构建及仿真验证,实现了对分布式电源、储能系统负荷的协同优化,兼顾经济性与可靠性目标,并通过仿真平台验证了所提方法的有效性与优越性。; 适合人群:具备电力系统、自动化或新能源相关专业背景,熟悉Matlab/Simulink仿真境,从事微电网能量管理、智能控制、能源优化等领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于高比例可再生能源接入场景下的微电网能量管理系统研发与教学实践;②为实现微电网功率稳定控制与经济高效运行提供先进的智能控制解决方案;③支撑高水平学术论文复现、科研课题攻关及实际工程项目的仿真验证与方案优化。; 阅读建议:建议结合提供的Simulink模型与相关代码进行动手实践,重点关注ANFIS控制器的设计流程、规则库构建与参数调优方法,并通过与传统PID或MPC控制策略的对比实验,深入理解其在动态响应与鲁棒性方面的优势。同时可进一步拓展文中提出的优化调度逻辑,应用于多目标、多约束的复杂实际应用场景中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值