首页 > 其他分享 >Living-Dream 系列笔记 第92期

Living-Dream 系列笔记 第92期

时间:2025-01-16 17:43:30浏览次数:1  
标签:Living include cur idx int vis 92 Dream match

最小路径点覆盖

在一张 DAG 上,求一个路径的集合,使得它们两两不相交,且覆盖所有的点。

结论:答案即为 \(总点数-最大匹配\)(于是 \(总点数-最大匹配=总点数-最小点覆盖=最大独立集=最大团=最小路径点覆盖\))。

证明:

不妨转换角度,从研究路径转为研究点。

因为路径两两不交,所以每条路径都会有一个唯一的终点。

因为 DAG 不一定是一张二分图,于是我们将其拆成二分图。

具体的,我们将每个点拆成入点(左部点)和出点(右部点),因为入点和出点自己内部没有连边,所以这一定是一张二分图。

我们知道终点一定没有出边,于是没有匹配的右部点就是终点。

我们要使路径数量最少,那么终点就要最少,于是匹配就得最多,这不就是最大匹配吗,而且它的不共点性质也帮我们处理了不相交的问题。

证毕

P2764

模板。

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

const int N=4e2+5;
int n,m,idx,ans;
vector<int> G[N];
int vis[N],match[N];
bool out[N];

bool hungary(int cur){
	if(vis[cur]==idx){
		return 0;
	}
	vis[cur]=idx;
	for(int i:G[cur]){
		if(!match[i]||hungary(match[i])){
			match[i]=cur;
			out[cur]=1;
			return 1;
		}
	}
	return 0;
}
void print(int cur){
	if(!match[cur+n]){
		cout<<cur<<' ';
		return;
	}
	print(match[cur+n]);
	cout<<cur<<' ';
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		G[u].push_back(v+n);
	}
	for(int i=1;i<=n;i++){
		idx++;
		if(hungary(i))
			ans++;
	}
	for(int i=1;i<=n;i++)
		if(!out[i])
			print(i),cout<<'\n';
	cout<<n-ans;
	return 0;
}

UVA1201

将每个乘客看作点,如果送完某个乘客后能赶到另一个乘客那里去,则将它们俩建边,形成了一个 DAG。将每个出租车司机的工作流程看作路径,显然两个出租车司机不能同时接一个乘客(不相交)且必须送完所有乘客(覆盖所有的点),直接上最小路径点覆盖即可。

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

const int N=1e3+5;
int t,n,m,idx,ans;
vector<int> G[N];
int vis[N],match[N];
bool out[N];
struct GUEST{
	int sta,sx,sy,ex,ey;
}g[N];

bool hungary(int cur){
	if(vis[cur]==idx){
		return 0;
	}
	vis[cur]=idx;
	for(int i:G[cur]){
		if(!match[i]||hungary(match[i])){
			match[i]=cur;
			out[cur]=1;
			return 1;
		}
	}
	return 0;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>t;
	while(t--){
		for(int i=1;i<=n;i++)
			G[i].clear();
		cin>>n;
		for(int i=1;i<=n;i++){
			string s;
			int sx,sy,ex,ey; 
			cin>>s>>g[i].sx>>g[i].sy>>g[i].ex>>g[i].ey;
			g[i].sta=((s[0]-'0')*10+(s[1]-'0'))*60+((s[3]-'0')*10+(s[4]-'0'));
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(i!=j){
					int ii=i,jj=j;
					if(g[ii].sta>g[jj].sta)
						swap(ii,jj);
					if(abs(g[ii].sx-g[ii].ex)+abs(g[ii].sy-g[ii].ey)+abs(g[jj].sx-g[ii].ex)+abs(g[jj].sy-g[ii].ey)<g[jj].sta-g[ii].sta)
						G[ii].push_back(jj);
				}
			}
		}
		ans=idx=0;
		memset(vis,0,sizeof vis);
		memset(match,0,sizeof match);
		for(int i=1;i<=n;i++){
			idx++;
			if(hungary(i))
				ans++;
		}
		cout<<n-ans<<'\n';
	}
	return 0;
}

OpenJ_Bailian-1548

这题没说路径不相交,但是不相交一定不劣。

然后就变成典题了,不再详细分析。

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

const int N=25;
int n,m,idx,ans;
int id[N][N];
pair<int,int> p[N*N];
vector<int> G[N*N*2];
int vis[N*N*2],match[N*N*2];

bool hungary(int cur){
	if(vis[cur]==idx){
		return 0;
	}
	vis[cur]=idx;
	for(int i:G[cur]){
		if(!match[i]||hungary(match[i])){
			match[i]=cur;
			return 1;
		}
	}
	return 0;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int tx,ty;
	while(cin>>tx>>ty&&(tx!=-1||ty!=-1)){
		memset(id,0,sizeof id);
		n=0;
		id[tx][ty]=++n;
		p[n]={tx,ty};
		while(cin>>tx>>ty&&(tx||ty)){
			id[tx][ty]=++n;
			p[n]={tx,ty};
		}
		for(int i=1;i<=N*N;i++)
			G[i].clear();
		for(int i=1;i<=n;i++){
			for(int j=p[i].first;j<N;j++){
				for(int k=p[i].second;k<N;k++){
					if(j==p[i].first&&k==p[i].second)
						continue;
					if(id[j][k]){
						G[id[p[i].first][p[i].second]].push_back(id[j][k]);
						break;
					}
				}
			}
		}
		ans=idx=0;
		memset(vis,0,sizeof vis);
		memset(match,0,sizeof match);
		for(int i=1;i<=n;i++){
			idx++;
			if(hungary(i))
				ans++;
		}
		cout<<n-ans<<'\n';
	}
	return 0;
}

HDU-3861

红色字体部分出现了很明显的强连通分量迹象,直接缩成 DAG。

对于一个 DAG,我们要求一个子图里的任意两点都是弱连通的,画一下图就会发现只有链可以满足这个条件,然后就变成典题了。

属于是板子拼接题了哈哈哈(唐诗题是这样的)。

code
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include <cwchar>
#include <cwctype>
using namespace std;

const int N=5e3+5,M=1e5+5;
int t,n,m;
int cnt,tot,idx,ans;
int dfn[N],low[N],instk[N],scc[N];
stack<int> s;
pair<int,int> p[N];
vector<int> G[N],T[N];
int vis[N],match[N];
struct EDGE{
	int u,v;
}e[M];

void tarjan(int v){
	s.push(v),instk[v]=1,dfn[v]=low[v]=++cnt;
	for(int i:G[v]){
		if(!dfn[i]) 
			tarjan(i),low[v]=min(low[v],low[i]);
		else if(instk[i]) 
			low[v]=min(low[v],dfn[i]);
	}
	if(dfn[v]==low[v]){
		++tot;
		for(;s.top()!=v;s.pop())
			instk[s.top()]=0,scc[s.top()]=tot;
		instk[v]=0,scc[v]=tot,s.pop();
	}
}
bool hungary(int cur){
	if(vis[cur]==idx){
		return 0;
	}
	vis[cur]=idx;
	for(int i:T[cur]){
		if(!match[i]||hungary(match[i])){
			match[i]=cur;
			return 1;
		}
	}
	return 0;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>t;
	while(t--){
		for(int i=1;i<=n;i++)
			G[i].clear(),T[i].clear();
		cin>>n>>m;
		for(int i=1,u,v;i<=m;i++){
			cin>>u>>v;
			G[u].push_back(v);
			e[i]={u,v};
		}
		cnt=tot=0;
		while(!s.empty())
			s.pop();
		memset(dfn,0,sizeof dfn);
		memset(low,0,sizeof low);
		memset(instk,0,sizeof instk);
		for(int i=1;i<=n;i++)
			if(!dfn[i])
				tarjan(i);
		for(int i=1;i<=m;i++)
			if(scc[e[i].u]!=scc[e[i].v])
				T[scc[e[i].u]].push_back(scc[e[i].v]);
		ans=idx=0;
		memset(vis,0,sizeof vis);
		memset(match,0,sizeof match);
		for(int i=1;i<=tot;i++){
			idx++;
			if(hungary(i))
				ans++;
		}
		cout<<tot-ans<<'\n';
	}
	return 0;
}

有相交的最小路径点覆盖

很简单,如图:

image

在这张图中,不相交的最小路径点覆盖的答案为 \(3\),而相交的答案为 \(2\),区别在于 \(4,5\) 两个节点是否在一条路径上。

实际上,我们仅仅关心路径数量而非方案(大多数情况下),于是我们直接将 \(4,5\) 连边即可转化为不相交的情形。

将其加以推广,我们仅需跑传递闭包得知所有直接或间接连通的点对,然后将它们之间加边使其全部转化为直接连通即可跑不相交的最小路径点覆盖。

P10938

板子。

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

const int N=2e2+5,M=3e4+5;
int n,m,idx,ans;
int dis[N][N];
vector<int> G[N],T[N];
int vis[N],match[N];

void floyd(){
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				dis[i][j]|=dis[i][k]&dis[k][j];
}
bool hungary(int cur){
	if(vis[cur]==idx){
		return 0;
	}
	vis[cur]=idx;
	for(int i:T[cur]){
		if(!match[i]||hungary(match[i])){
			match[i]=cur;
			return 1;
		}
	}
	return 0;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		G[u].push_back(v);
		dis[u][v]=1;
	}
	floyd();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(dis[i][j])
				T[i].push_back(j);
	for(int i=1;i<=n;i++){
		idx++;
		if(hungary(i))
			ans++;
	}
	cout<<n-ans<<'\n';
	return 0;
}

OpenJ_Bailian-3473

有点诈骗吧。本来以为给了个邻接矩阵,以为是相交的,结果是不相交的。

其实和 UVA1201 一致,只是不同街区的行走需要求一个最短路。

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

const int Q=31,M=2e2+5;
int q,m;
int idx,ans;
int vis[M],match[M];
int dis[Q][Q];
struct TASK{
	int p,t,d;
}task[M];
vector<int> G[M];

void floyd(){
	for(int k=1;k<=q;k++)
		for(int i=1;i<=q;i++)
			for(int j=1;j<=q;j++)
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
bool hungary(int cur){
    if(vis[cur]==idx){
        return 0;
    }
    vis[cur]=idx;
    for(int i:G[cur]){
        if(!match[i]||hungary(match[i])){
            match[i]=cur;
            return 1;
        }
    }
    return 0;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	while(cin>>q>>m){
		if(!q&&!m)
			break;
		for(int i=1;i<=q;i++){
			for(int j=1;j<=q;j++){
				 cin>>dis[i][j];
				 dis[i][j]=(dis[i][j]==-1?0x3f3f3f3f:dis[i][j]);
			}
		}
		for(int i=1;i<=m;i++)
			cin>>task[i].p>>task[i].t>>task[i].d,G[i].clear();
		floyd();
		for(int i=1;i<=m;i++)
			for(int j=1;j<=m;j++)
				if(i!=j&&task[i].t+task[i].d+dis[task[i].p][task[j].p]<=task[j].t)
					G[i].push_back(j);
		ans=idx=0;
		memset(vis,0,sizeof vis);
		memset(match,0,sizeof match);
		for(int i=1;i<=m;i++){
			idx++;
			if(hungary(i))
				ans++;
		}
		cout<<m-ans<<'\n';
	}
	return 0;
}

总结:

  • 图论题想不明白时画图。

  • 注意题中关键信息(解读条件)。

  • 注意最小路径点覆盖问题区分相交 / 不相交,以及它和其他问题之间的关系(上文已给出)。

标签:Living,include,cur,idx,int,vis,92,Dream,match
From: https://www.cnblogs.com/XOF-0-0/p/18675473

相关文章

  • Living-Dream 系列笔记 第91期
    最大团一张图的最大团被定义为它的一个极大的完全子图。对于任意一张图,我们都有如下结论:其最大团就是其补图的最大独立集。读者自证不难。于是乎,只要一张图的补图为二分图,我们就可以轻松求出它的最大团。P2764抽象一下题意,我们发现我们需要连一条边使得最大团的大小加一。在......
  • 计算机毕业设计—92713 Springboot邻家优选超市线上线下购物系统小程序(源码免费领)
    摘 要21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,并能提高工作管理效率,促进其发展。论文主要是对邻家优选超......
  • 计算机毕业设计—92968 高校运动会信息管理系统设计与实现(源码免费领)
    摘 要本论文介绍了一个高校运动会信息管理系统的设计和实现过程。首先是高校运动会的需求分析和可行性分析,通过比较运动会的各个工作流程,确定了系统的数据流程和数据库结构,然后介绍了高校运动会信息管理系统开发所使用的软件开发工具,最后描述了系统的详细设计与实现。本......
  • 计算机毕业设计—92767 php 酒店预约管理系统 (源码免费领)
    摘 要随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,酒店预约管理系统当然也不能排除在外。酒店预约管理系统是以实际运用为开发背景,运用软件工程开发方法,采用Thinkphp技术构建的一个管理系统。整个开发过程......
  • 【开源】基于SpringBoot框架电商平台(计算机毕业设计)+万字毕业论文 T192
    系统合集跳转源码获取链接点击主页更能获取海量源码10年计算机开发经验,主营业务:源码获取、项目二开、语音辅导、远程调试、毕业设计、课程设计、毕业论文、BUG修改一、系统环境运行环境:最好是javajdk1.8,我们在这个平台上运行的。其他版本理论上也可以。IDE环境......
  • gesp(C++五级)(5)洛谷:B3929:[GESP202312 五级] 小杨的幸运数
    gesp(C++五级)(5)洛谷:B3929:[GESP202312五级]小杨的幸运数题目描述小杨认为,所有大于等于aaa的完全平方数都是他的超级幸运数。小杨还认为,所有超级幸运数的倍数都是他......
  • [1092] Git Tutorial
    Ref:GitTutorial-W3SchoolUsingGitwithCommandLinegit--versiongitversion2.30.2.windows.1ConfigureGitgitconfig--globaluser.name"w3schools-test"gitconfig--globaluser.email"test@w3schools.com"CreatingGitFolderm......
  • Codeforces Round 992 (Div. 2) C题解析
    CodeforcesRound992(Div.2) C题解析题目描述......
  • JSP离退休管理系统7z292--(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表技术要求:开发语言:JSP前端使用:HTML5,CSS,JSP动态网页技术后端使用SpringBoot,Spring技术主数据库使用MySQL开题报告内容一、研究背景与意义随着人口老龄化趋......
  • Living-Dream 系列笔记 第90期
    鲜花:其实一直想改一下笔记的形式,以一个算法专题作为一篇博文的内容。这个系列到100期就完结吧。二分图最大独立集选择最多的点,使得这个点集中的点互相没有连边。答案显然为\(n-最小点覆盖=n-最大匹配\)(\(n\)为总点数)。但是好像最小点覆盖那一期忘记写了,所以解释一下为什么......