【专题属性】图论

本文深入探讨图论中的FloodFill算法、拓扑排序以及最短路问题。通过实例解析POJ_2386、UVA11893、POJ_3249等题目,讲解如何利用图的性质解决复杂问题,包括Dagy图的判断、最小生成树构造以及2-SAT算法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



1.FloodFill


这一部分没什么好说的,大部分都是水题。


题目:POJ_2386 


2.拓扑排序


Fabulous DAGyUVA11893

这一题的大概意思就是一种叫做Dagy的有向图,满足以下性质,删去其中的一条边后是有向无环图,且这个图存在一个环,这个环能够包含图上的所有节点。问输入的图是否满足性质。


题解:

1.如果存在入度为0的结点,则一定不是Dagy图。(该点必定无法被包含在环内)

2.枚举所有入度为1的结点,删去指向它的边(不用真的删去),则它的入度为0.

3.对某一次删除,结点U入度为0。从u开始做拓扑排序。若某一时刻,存在>=2个结点入度为0,则这两个点一定不在同一个环内。则这次删除不符合性质,返回到第2步。若某一时刻,对于入度为0的点u,存在边(u,v),v=U,则说明返回到了起始点,此时判断经过的点个数是否等于N,是则满足性质,否则返回第二步。


#include <iostream>
#include <cstring>
#define maxn 405
using namespace std;

int e,now,n,m,uu,u1;
int f[maxn];
int visit[maxn]; 
int ru[maxn],r1[maxn],chu[maxn],q[maxn];
int a[maxn][maxn];


int main()
{
    int tt,t;
	cin>>tt;
	while (tt--)
	{
		memset(ru,0,sizeof(ru));
		memset(chu,0,sizeof(chu));
		memset(f,0,sizeof(f));
		memset(a,0,sizeof(a));
		cin>>n>>m;
	    while (m--)
		{
            int x,y;
			cin>>x>>y;
			chu[x]++;
			ru[y]++;
           	a[x][y]=1;
		}	
	int i;
	for (i=0;i<n;i++)
    if 	(ru[i]==0)
	break;


        if (i==n)
        {
       for (i=0;i<n;i++)
       if (ru[i]==1) 
       {
		int now,now1;
		for (int j=0;j<n;j++)
		   r1[j]=ru[j];
		   now=i;
		   t=1;
		   int tot=0;
		   while (t==1)
		   {
				for (int j=0;j<n;j++)
				if (a[now][j])
				{
					r1[j]--;
					if (r1[j]==0)
					{
                        			tot++;
						t++;
						now1=j;
						if (t>2) break;
					}
				}
				t--;
				if (t>1) break;
				now=now1;
				if (tot==n  && now==i)  t=-1;
			}
			if (t==-1) break;
        }
        
       // uu=i;
          

		if (t==-1)
			cout<<"Yeah, I'm superman"<<endl;else
			cout<<"Your DAGy was initially defected!"<<endl; 
    } else 	cout<<"Your DAGy was initially defected!"<<endl; 
}
	//while (1);
    return 0;
}


/*
100
5 5
0 1 
1 2
1 3
3 4
4 0

5 6
0 1
1 2
2 3
3 4
1 4
4 0

*/


题目:POJ_3249


3.MST(最小生成树)


Qin Shi Huang's National Road System(hdu_4081)

题目大意是给出一张图,每个点都有一个权值,以及点的坐标(x,y),通过一条边将其中的两个点连接起来(magic road),且这条边代价为0,并用n-2条边使整个图联通,这n-2条边的代价为连接的两个点的距离。假设magic road连接的两个点权值分别为a,b,n-2条边的代价和为cost,求(a+b)/cost的最大值


题解:

先得到这个图的最小生成树,O(N^2),初始ans为INF,枚举添加的边E(u,v),则添加的边与最小生成树会形成一个环,删除环上代价最大的边(prim的同时用path[i][j]预处理出来就好),则得到的值为(a+b)/(tot-path[u][v]),若小于ans则更新,a,b为u,v的权值,tot为最小生成树的总代价,path[u][v]为环上代价最大的边。答案即为ans。


#include <iostream>
#include <cmath>
#include <cstdio>
#define maxn 1005
#define MAX 1000000000
#define max(a,b) (a)>(b)?(a):(b)

using namespace std;

double path[maxn][maxn];
int visit[maxn],pre[maxn],x[maxn],y[maxn],p[maxn];
double a[maxn][maxn],dis[maxn];

int main()
{
	int tt;
	cin>>tt;
	while (tt--)
	{
        int n;
		cin>>n;
		for (int i=1;i<=n;i++)
		{
			cin>>x[i]>>y[i]>>p[i];
			//graph[x].clear();
		}		
		
		//index=-1;
		for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		{
			//node x;
			double p=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
			//x.val=sqrt(p);
			//x.pos=j;
			//graph[x].push_back(x);
			a[i][j]=sqrt(p);
			path[i][j]=0;
		}
		for (int i=1;i<=n;i++)
		{
			dis[i]=MAX;
			visit[i]=0;
			pre[i]=i;
		}
	//	memset(path,0,sizeof(path));
		dis[1]=0;
		
		double tot=0;
		int u;
		for (int i=1;i<=n;i++)
		{
		      double	min=MAX;
			for (int j=1;j<=n;j++)
			if (!visit[j] && dis[j]<min)
			{
				min=dis[j];
				u=j;
			}
			visit[u]=1;
			tot+=min;
			int k=pre[u];
			for (int j=1;j<=n;j++)
			if (u!=j && visit[j])
					path[u][j]=path[j][u]=max(path[j][k],min);
			for (int j=1;j<=n;j++)
			if (!visit[j] && a[u][j]<dis[j])
			{
				dis[j]=a[u][j];
				pre[j]=u;
			}
		}
		
		double ans=0;
		for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
		{
			double p1=(p[i]+p[j])*1.0/(tot-path[i][j]);
			if (p1>ans) ans=p1;
		}
		printf("%.2lf\n",ans);
	}
//	while (1);
	return 0;
}




题目: POJ_2349


4.最短路


差分约束:(POJ_1201POJ_1275POJ_2983

这一类型题没什么好说的,建好图套一个SPFA或者bellman-ford即可。


5.强连通分量


Ikki's Story IV - Panda's Trick (hdu_4081)

题意:平面上,一个圆,圆的边上按顺时针放着n个点。现在要连m条边,比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接。给你的信息中,每个点最多只会连接的一条边。问能不能连接这m条边,使这m条边不相交。


将每条边拆成i,i',i表示从内部连接,i'表示外部连接,那么对于i,j,如果i,j放在一边会相交,则i->j',i'->j,j->i',j'->i,连接这四条边,然后就是2-sat算法了。


#include <iostream>
#include <cstring>
#include <vector>
#define MIN(a,b) (a)<(b)?(a):(b)
#define maxn 1005

using namespace std;

vector <int> graph[maxn];
int visit[maxn],q[maxn],que[maxn],c[maxn],dfn[maxn],low[maxn],index,tail,color;

struct node
{
    int l,r;
} e[maxn];

void insert(int u,int v)
{
        graph[u].push_back(v);
}


void tarjan(int u)
{
    dfn[u]=low[u]=++index;
    visit[u]=1;
    q[++tail]=u;
    que[u]=1;
    for (int i=0;i<graph[u].size();i++)
    {
        int p=graph[u][i];
        if (!visit[p])
        {
            tarjan(p);
            low[u]=MIN(low[u],low[p]);
        } else
        if (que[p])
        low[u]=MIN(low[u],dfn[p]);
    }

    int y;
    if (low[u]==dfn[u])
    {
        color++;
        do
        {
           y=q[tail];
           que[y]=0;
           c[y]=color;
           tail--;
        } while (y!=u);
    }
}

int main()
{
    int n,m;
    cin>>n>>m;
    for (int i=1;i<=m;i++)
    {
        cin>>e[i].l>>e[i].r;
        if (e[i].l>e[i].r) {int temp=e[i].l;e[i].l=e[i].r;e[i].r=temp;}
    }

    for (int i=1;i<=2*m;i++)
    graph[i].clear();

    for (int i=1;i<=m-1;i++)
    for (int j=i+1;j<=m;j++)
    {
        if (e[i].l<e[j].l && e[i].r>e[j].l && e[i].r<e[j].r ||
            e[i].l>e[j].l && e[i].r>e[j].r && e[i].l<e[j].r)
        {
            insert(i,j+m);
            insert(i+m,j);
            insert(j,i+m);
            insert(j+m,i);
        }
    }

    memset(visit,0,sizeof(visit));
    memset(q,0,sizeof(q));
    memset(que,0,sizeof(que));

    tail=-1;index=0;color=0;
    for (int i=1;i<=2*m;i++)
    if (!visit[i])
    tarjan(i);

    int i;
    for (i=1;i<=m;i++)
    if (c[i]==c[i+m]) break;

    if (i<=m) cout<<"the evil panda is lying again"<<endl;else
    cout<<"panda is telling the truth..."<<endl;

    return 0;
}

题目: POJ_2186  POJ_1236




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值