zoukankan      html  css  js  c++  java
  • 它们其实都是“图”-- 最小生成树

    1、POJ 3169

    题意:

    n头牛编号为1到n,按照编号的顺序排成一列,每两头牛的之间的距离 >= 0。这些牛的距离存在着一些约束关系:1.有ml组(u, v, w)的约束关系,表示牛[u]和牛[v]之间的距离必须 <= w。2.有md组(u, v, w)的约束关系,表示牛[u]和牛[v]之间的距离必须 >= w。问如果这n头无法排成队伍,则输出-1,如果牛[1]和牛[n]的距离可以无限远,则输出-2,否则则输出牛[1]和牛[n]之间的最大距离。

    分析:

    记第i号牛的位置是d[i]。首先,牛是按照编号顺序排列的,所以有d[i]<=d[i+1]成立。其次,对于每对关系好的牛之间的最大距离限制,都有d[AL]+DL >=d[BL]成立。同样,对于每对关系不好的牛,都有d[AD]+DD<=d[BD]成立。因此,原问题可以转化为在满足这三类不等式的情况下,求解d的d[N] -d[1]的最大值问题。这是线性规划问题,可以使用单源最短路径算法求解。

    这些不等式的特点是所有的式子的两边都只出现了1个变量。实际上,图上的最短路问题也可以用这样的形式表示出来。记从起点s出发,到各个顶点v的最短距离为d(v)。因此,对于每条权值为w的边e=(v,u),都有d(v)+w >=d(u)成立。反之,在满足全部这些约束不等式的d中,d(v)-d(s)的最大值就是从s到v的最短距离。需要注意这里不是最小值,而是最大值对应着最短距离。

    把原来的问题和最短路问题进行比较就可以发现,两个问题都是完全一样的形式。也就是说,可以通过把原来的问题的每一个约束不等式对应成图中的一条边来构图,然后通过解决最短路问题来解决原问题。首先把顶点编号为1~N,d[i]<=d[i+1]变形为d[i+1]+0>=d[i],因此从顶点i+1向顶点i连一条权值为0的边。同样d[AL]+DL>=d[BL]对应从顶点BL连一条权值为DL的边,d[AD]+DD<=d[BD]对应从顶点BD向顶点AD连一条权值为-DD的边。所求的问题是d[N]-d[1]的最大值,对应为顶点1到顶点N的最短距离。由于图中存在负权边,因此不使用Dijkstra算法而是使用Bellman-Ford算法求解。即使这样复杂度也只有O(N(N+ML+MD))。

    用求最短路和最长路解差分约束问题

    1)建图后求最短路(对应 <= 的差分约束)

    从求最短路后分析开始。求最短路后一定有:d(u) + w(u,v) >= d(v) 转换为:d(v) – d(u) <= w(u,v)。然后这个式子与我们差分约束条件得到的不等式( X(a) - X(b) <= c )相似。

    所以得到建图的规则为:一条b到a的边权值为c。

    建图后求最短路即为一组解。

    2)建图后求最长路(对应 >= 的差分约束)

    同上。求最长路后一定得到:d(u) + w(u,v) <= d(v) 转换为:d(v)-d(u)>=w(u,v)。然后对比差分约束条件的不等式( X(a) - X(b) >= c )。

    所以得到建图的规则为:一条b到a的边权值为c。

    建图后求最长路即为一组解。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int MAX_N = 10000 + 10;
    const int INF = 1000000000;
    
    int N,ML,MD;
    int AL[MAX_N], BL[MAX_N], DL[MAX_N];
    int AD[MAX_N], BD[MAX_N], DD[MAX_N];
    int d[MAX_N];
    
    void solve()
    {
    	fill(d,d+N,INF);
    	d[0]=0;
    
    	for(int k=0;k<N;k++)
    	{
    		for(int i=0;i+1<N;i++){
    			if(d[i+1]<INF)
    				d[i]=min(d[i],d[i+1]);
    		}
    
    		for(int i=0;i<ML;i++){
    			if(d[AL[i]-1]<INF)
    				d[BL[i]-1]=min(d[BL[i]-1], d[AL[i]-1]+DL[i]);
    		}
    
    		for(int i=0;i<MD;i++){
    			if(d[BD[i]-1]<INF)
    				d[AD[i]-1]=min(d[AD[i]-1], d[BD[i]-1]-DD[i]);
    		}
    	}
    	int res=d[N-1];
    	if(d[0]<0)
    		res=-1;
    	else if(res==INF)
    		res=-2;
    	printf("%d
    ",res);
    }
    
    int main(){
    	while(~scanf("%d%d%d",&N,&ML,&MD))
    	{
    		for(int i=0;i<ML;i++)
    			scanf("%d%d%d",&AL[i],&BL[i],&DL[i]);
    		for(int i=0;i<MD;i++)
    			scanf("%d%d%d",&AD[i],&BD[i],&DD[i]);
    		solve();
    	}
    
    	return 0;
    
    }
    

      

    2、POJ 1258

    题意:求建立光纤的最小长度。

    思路:用prim算法求解最小生成树。

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int INF = 0x3f3f3f3f;
    const int MAX_N = 100 + 5;
    
    int n,ans=0;
    int m[MAX_N][MAX_N];
    bool used[MAX_N];
    int dis[MAX_N];
    
    void prim()
    {
    	for(int i=1;i<=n;i++)
    		dis[i]=m[1][i];
    	dis[1]=0;
    	used[1]=true;
    	for(int v=2;v<=n;v++)
    	{
    		int minn=INF;
    		int p=-1;
    		for(int i=1;i<=n;i++)
    		{
    			if(!used[i] && dis[i]<minn)
    			{
    				p=i;
    				minn=dis[i];
    			}
    			
    		}
    		used[p]=true;
    		ans+=dis[p];
    		for(int i=1;i<=n;i++)
    			if(!used[i] && dis[i]>m[p][i])
    				dis[i]=m[p][i];
    	}
    }
    
    int main()
    {
    	while(~scanf("%d",&n))
    	{   
    		memset(used,false,sizeof(used));
    		memset(m,INF,sizeof(m));
    		ans=0;
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=n;j++)
    				scanf("%d",&m[i][j]);
    		prim();
    		printf("%d
    ",ans);
    	}
    
    	return 0;
    }
    

      

    3、POJ 2377

    题意:求最大生成树

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int INF = 0x3f3f3f3f;
    const int MAX_N = 1000 + 5;
    
    int maps[MAX_N][MAX_N],dis[MAX_N];
    bool v[MAX_N];
    int m,n;
    
    int prim()
    {
    	for(int i=1;i<=n;i++)
    		dis[i]=maps[1][i];
    	v[1]=true;
    	int sum=0;
    
    	for(int i=1;i<n;i++)
    	{
    		int index=-1;
    		int minn=-INF;
    
    		for(int j=1;j<=n;j++)
    		{
    			if(!v[j] && dis[j]>minn)
    			{
    				minn=dis[j];
    				index=j;
    			}
    		}
    
    		if(minn == -1)
    			return -1;
    		
    		sum += minn;
    		v[index]=true;
    
    		for(int j=1;j<=n;j++)
    		{
    			if(!v[j] && dis[j]<maps[index][j])
    				dis[j]=maps[index][j];
    		}
    	}
    	return sum;
    }
    
    int main()
    {
    	int a,b,c;
    	scanf("%d%d",&n,&m);
    
    	memset(maps,-1,sizeof(maps));
    
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&a,&b,&c);
    		maps[a][b]=maps[b][a]=max(maps[a][b],c);
    	}
    	
    	memset(v,false,sizeof(v));
    	int ans=prim();
    	printf("%d
    ",ans);
    
    
    	return 0;
    }
    

      

    4、AOJ 2224

    题目:

      有很多猫被困在一个图中,这些图由若干点组成,然后给了若干连线,把这个图分为了几个区域,请你求出把这个图消去若干条边,最后使这个图没有封闭区域,请问最小的花费是多少(花费等于边长总和)

    思路:

      一开始并没有头绪,看了别人的题解发现把一个图变成一个没有封闭区域的图的最小花费=图的总权值-最大生成树的花费!这个真的没有想到

    Problem C: Save your cats
    Nicholas Y. Alford was a cat lover. He had a garden in a village and kept many cats in his garden. The cats were so cute that people in the village also loved them.
    
    One day, an evil witch visited the village. She envied the cats for being loved by everyone. She drove magical piles in his garden and enclosed the cats with magical fences running between the piles. She said “Your cats are shut away in the fences until they become ugly old cats.” like a curse and went away.
    
    Nicholas tried to break the fences with a hummer, but the fences are impregnable against his effort. He went to a church and asked a priest help. The priest looked for how to destroy the magical fences in books and found they could be destroyed by holy water. The Required amount of the holy water to destroy a fence was proportional to the length of the fence. The holy water was, however, fairly expensive. So he decided to buy exactly the minimum amount of the holy water required to save all his cats. How much holy water would be required?
    
    Input
    The input has the following format:
    
    N M
    x1 y1
    .
    .
    .
    xN yN
    p1 q1
    .
    .
    .
    pM qM
    The first line of the input contains two integers N (2 ≤ N ≤ 10000) and M (1 ≤ M). N indicates the number of magical piles and M indicates the number of magical fences. The following N lines describe the coordinates of the piles. Each line contains two integers xi and yi (-10000 ≤ xi, yi ≤ 10000). The following M lines describe the both ends of the fences. Each line contains two integers pj and qj (1 ≤ pj, qj ≤ N). It indicates a fence runs between the pj-th pile and the qj-th pile.
    
    You can assume the following:
    
    No Piles have the same coordinates.
    A pile doesn’t lie on the middle of fence.
    No Fences cross each other.
    There is at least one cat in each enclosed area.
    It is impossible to destroy a fence partially.
    A unit of holy water is required to destroy a unit length of magical fence.
    Output
    Output a line containing the minimum amount of the holy water required to save all his cats. Your program may output an arbitrary number of digits after the decimal point. However, the absolute error should be 0.001 or less.
    
    Sample Input 1
    3 3
    0 0
    3 0
    0 4
    1 2
    2 3
    3 1
    Output for the Sample Input 1
    3.000
    Sample Input 2
    4 3
    0 0
    -100 0
    100 0
    0 100
    1 2
    1 3
    1 4
    Output for the Sample Input 2
    0.000
    Sample Input 3
    6 7
    2 0
    6 0
    8 2
    6 3
    0 5
    1 7
    1 2
    2 3
    3 4
    4 1
    5 1
    5 4
    5 6
    Output for the Sample Input 3
    7.236
    Sample Input 4
    6 6
    0 0
    0 1
    1 0
    30 0
    0 40
    30 40
    1 2
    2 3
    3 1
    4 5
    5 6
    6 4
    Output for the Sample Input 4
    31.000
    题目
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    const int INF = 0x3f3f3f3f;
    const int MAX_N = 1e4 + 5;
    const int MAX_M = 1e6 + 5;
    
    int n,m;
    double sum=0.0,ans=0.0;
    int x[MAX_N],y[MAX_N];
    
    struct edge{
    	int v,t;
    	double dist;
    }e[MAX_M];
    
    bool cmp(const edge a,const edge b)
    {
    	return a.dist>b.dist;
    }
    
    int par[MAX_N];
    
    int find(int x)
    {
    	if(par[x]==x) return x;
    	return par[x]=find(par[x]);
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    		scanf("%d%d",&x[i],&y[i]);
    	
    	for(int i=0;i<m;i++)
    	{
    		scanf("%d%d",&e[i].v,&e[i].t);
    		e[i].dist=sqrt((x[e[i].v]-x[e[i].t])*(x[e[i].v]-x[e[i].t])+(y[e[i].v]-y[e[i].t])*(y[e[i].v]-y[e[i].t]));
    		sum+=e[i].dist;
    	}
    	sort(e,e+m,cmp);
    	for(int i=0;i<n;i++) par[i]=i;
    	for(int i=0;i<m;i++)
    	{
    		int fx=find(e[i].v);
    		int fy=find(e[i].t);
    
    		if(fx!=fy)
    		{
    			ans+=e[i].dist;
    			par[fx]=fy;
    		}
    	}
    
    	printf("%.3f",sum-ans);
    
    
    	return 0;
    }
    

      

    5、POJ 2395

     思路:最小生成树的最大边

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    const int INF = 0x3f3f3f3f;
    const int MAX_N = 2e3 + 5;
    const int MAX_M = 1e4 + 5;
    
    struct node
    {
    	int u,v,cost;
    }a[MAX_M];
    
    bool cmp(node a,node b)
    {
    	return a.cost<b.cost;
    }
    
    int par[MAX_N];
    
    int find(int x)
    {
    	if(x==par[x])	return x;
    	return par[x]=find(par[x]);
    }
    
    void unite(int x,int y)
    {
    	int fx=find(x);
    	int fy=find(y);
    	if(fx!=fy)
    		par[fx]=fy;
    }
    
    int main()
    {
    	int n,m;
    	scanf("%d%d",&n,&m);
    
    	for(int i=0;i<m;i++)
    		scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].cost);
    	sort(a,a+m,cmp);
    
    	for(int i=1;i<=n;i++)	par[i]=i;
    
    	int ans=0;
    	for(int i=0;i<m;i++)
    	{
    		node b=a[i];
    		if(find(b.u) != find(b.v))
    		{
    			unite(b.u,b.v);
    			ans=max(ans,b.cost);
    		}
    	}
    	printf("%d
    ",ans);
    
    	return 0;
    }
    

      

  • 相关阅读:
    这个是我得标题:1548669163
    Mahout学习
    MySQL
    Ubuntu
    java小程序100例
    java实现链表从尾部输出
    空格替换
    java 实现二维数组查找
    JAVA实现分页
    java 程序参数详解
  • 原文地址:https://www.cnblogs.com/jaszzz/p/12879661.html
Copyright © 2011-2022 走看看