图论从0.5入门

作为退役菜鸡,在队内不负责图论部分,所以图论部分菜之又菜。现在单挑时遇到图论的题只能自认倒霉,做不出来呀!
痛定思痛!决心好好学习图论!
首先是Dijkstra、Floyd、Prmie、Kruskal、拓扑排序,这怎么可能不会呢~~so直接跳过

先学个匈牙利算法

(解二分图最大匹配)
洛谷:【模板】二分图最大匹配

int m,n,e;
vector<int>vec[maxn];
bool vs[maxn];
int match[maxn];
queue<int>q;
bool dfs(int x)
{
	for(int j:vec[x])
	{
		if(vs[j])continue;
		vs[j]=1;q.push(j);
		if(!match[j]||dfs(match[j]))
		{
			match[j]=x;
			return 1;
		}
	}
	return 0;
}
void solve()
{
	scanf("%d%d%d",&n,&m,&e);
	for(int i=1;i<=e;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		v+=n;
		vec[u].pb(v);
		vec[v].pb(u);
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		while(!q.empty())vs[q.front()]=0,q.pop();
		ans+=dfs(i);
	}
	printf("%d\n",ans);
}

洛谷:飞行员配对方案问题

int m,n;
vector<int>vec[maxn];
bool vs[maxn];
int match[maxn];
queue<int>q;
bool dfs(int x)
{
	for(int j:vec[x])
	{
		if(vs[j])continue;
		vs[j]=1;q.push(j);
		if(!match[j]||dfs(match[j]))
		{
			match[j]=x;
			return 1;
		}
	}
	return 0;
}
void solve()
{
	scanf("%d%d",&m,&n);
	n-=m;
	int u,v;
	while(scanf("%d%d",&u,&v)&&u!=-1)
	{
		vec[u].pb(v);
		vec[v].pb(u);
	}
	int ans=0;
	for(int i=1;i<=m;i++)
	{
		while(!q.empty())vs[q.front()]=0,q.pop();
		ans+=dfs(i);
	}
	printf("%d\n",ans);
	for(int i=m+1;i<=m+n;i++)
	{
		if(match[i])printf("%d %d\n",i,match[i]);
	}
}

接下来就是KM算法啦

(解二分图最大匹配中的最大权值和)
HDU2255奔小康赚大钱

int n;
int v[maxn][maxn];
bool vx[maxn],vy[maxn];
int match[maxn],mi;
int wx[maxn],wy[maxn];
bool dfs(int x)
{
	vx[x]=1;
	for(int i=1;i<=n;i++)
	{
		if(vy[i])continue;
		int tmp=wx[x]+wy[i]-v[x][i];
		if(tmp==0)
		{
			vy[i]=1;
			if(!match[i]||dfs(match[i]))
			{
				match[i]=x;
				return 1;
			}
		}
		else if(tmp>0)
		{
			mi=min(mi,tmp);
		}
	}
	return 0;
}
void solve()
{
	memset(wx,0,sizeof wx);
	memset(wy,0,sizeof wy);
	memset(match,0,sizeof match);
	memset(vx,0,sizeof vx);
	memset(vy,0,sizeof vy);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
	{
		scanf("%d",&v[i][j]);
		wx[i]=max(wx[i],v[i][j]);
	}
	for(int i=1;i<=n;i++)
	{
		while(1)
		{
			memset(vx,0,sizeof vx);
			memset(vy,0,sizeof vy);
			mi=1e9;
			if(dfs(i))break;
			for(int j=1;j<=n;j++)
			{
				if(vx[j])wx[j]-=mi;
				if(vy[j])wy[j]+=mi;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)ans+=v[match[i]][i];
	printf("%d\n",ans);
}
int main()
{
	int T=1;
//	scanf("%d",&T); 
//	while(T--)solve();
	while(~scanf("%d",&n))solve();
}

洛谷:分配问题

int n,c[maxn][maxn];
bool vx[maxn],vy[maxn];
int match[maxn];
int wx[maxn],wy[maxn],mi=1e9;
bool dfs(int x)
{
	vx[x]=1;
	for(int i=1;i<=n;i++)
	{
		if(vy[i])continue;
		int tmp=wx[x]+wy[i]-c[x][i];
		if(!tmp)
		{
			vy[i]=1;
			if(!match[i]||dfs(match[i]))
			{
				match[i]=x;
				return 1;
			}
		}
		else if(tmp>0)
		{
			mi=min(mi,tmp);
		}
	}
	return 0;
}
int KM()
{
	for(int i=1;i<=n;i++)
	{
		while(1)
		{
			memset(vx,0,sizeof vx);
			memset(vy,0,sizeof vy);
			if(dfs(i))break;
			for(int j=1;j<=n;j++)
			{
				if(vx[j])wx[j]-=mi;
				if(vy[j])wy[j]+=mi;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)ans+=c[match[i]][i];
	return ans;
}
void solve()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
	{
		scanf("%d",&c[i][j]);
		wx[i]=max(wx[i],c[i][j]);
	}
	int ans1=KM();
	memset(wy,0,sizeof wy);
	for(int i=1;i<=n;i++)
	{
		wx[i]=-1e9;
		for(int j=1;j<=n;j++)
		{
			c[i][j]*=-1;
			wx[i]=max(wx[i],c[i][j]);
		}
	}
	memset(match,0,sizeof match);
	mi=1e9;
	int ans2=-KM();
	printf("%d\n%d\n",ans2,ans1);
}

有向图缩点tarjan

洛谷:【模板】缩点

int n,m,a[maxn],num,ans;
vector<int>vec[maxn];
int dfn[maxn],low[maxn],cnt;
bool vis[maxn];
int sta[maxn],stk;
int be[maxn],val[maxn],in[maxn],ma[maxn];
int u[maxn],v[maxn];
void tarjan(int x)
{
	dfn[x]=low[x]=++cnt;
	sta[++stk]=x;
	vis[x]=1;
	for(int j:vec[x])
	{
		if(!dfn[j])
		{
			tarjan(j);
			low[x]=min(low[x],low[j]);
		}
		else if(vis[j])
		{
			low[x]=min(low[x],low[j]);
		}
	}
	if(dfn[x]==low[x])
	{
		++num;
		while(1)
		{
			int y=sta[stk--];
			be[y]=num;
			val[num]+=a[y];
			vis[y]=0;
			if(x==y)break;
		}
	}
}
void topu()
{
	queue<int>q;
	for(int i=1;i<=n;i++)if(!in[i])q.push(i);
	while(!q.empty())
	{
		int p=q.front();q.pop();
		val[p]+=ma[p];
		ans=max(ans,val[p]);
		for(int j:vec[p])
		{
			in[j]--;
			ma[j]=max(ma[j],val[p]);
			if(!in[j])q.push(j);
		}
	}
}
void solve()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u[i],&v[i]);
		vec[u[i]].pb(v[i]);
	}
	for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;i++)vec[i].clear();
	for(int i=1;i<=m;i++)
	{
		if(be[u[i]]==be[v[i]])continue;
		vec[be[u[i]]].pb(be[v[i]]);
		in[be[v[i]]]++;
	}
	topu();
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值