zoukankan      html  css  js  c++  java
  • C++ 差分约束系统

    最近开始学差分约束系统了,讲课时检验学习程度的一个好方法,于是我就在博客园试图讲一讲。

    首先,我们要知道差分约束系统是什么:

    如果一个不等式组由n个变量和m个约束条件形成,形成m个如a-b<=c(c是常数)的不等式,我们就称他为差分约束系统。

    说简单点,差分约束系统就是求解一组符合不等式组的变量。

    差分约束系统可以用图论算法中的最长路解。这次我们用SPFA。

    我们知道了差分约束系统可以用最短路来解,那么就需要建图,建好图跑最短路就可以了,但问题是怎么建图呢?

    要解决一个问题,可以举一些例子来帮助斯烤。我就来举一个a-b<=c的例子。

    虽然这样子我们还是不知道怎么建图,但我们可以通过转化来把他转化成已知的知识,这样就可以知道如何建图。

    我们可以把a-b<=c转化成a<=b+c,是不是,这样转化完,我们可以发现他和最短路的一个性质好像哦。就是zd(i)<=zd(j)+lj(i,j),嗯……用能看的懂的话翻译一下就是,一个点的最短路径,肯定小于等于另一个点的最短路径+过来的长度。届时,我们把a<=b+c代入一下,就可以知道如何建图了,那就是从b到a之间连接一个长度为c的路径。

    那如果出现a-b>=c这种情况呢?不要慌,我们可以利用数学上学过的定律(具体那个我忘了),我们可以把>=两边的数都*-1,这个式子就会由a-b>=c变成b-a<=-c。然后我们就会做了。是不是很简单!

    如果出现a-b=0这种情况直接都连就好了。

    在跑之前,还要创造一个超级节点,来维持图的联通性。这样一来,整个图就建好了,我们只要跑一遍SPFA就可以得到想要的结果了。

    好了讲完了。

    哦哦还有代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    queue<long long>p;
    long long n,m,a,b,c,bj[5005],sz[5005],cs[5005],wz;
    long long ans,head[5005];
    struct hehe
    {
    	long long w,cd,syg;
    }lsqxx[10005];//边最多是10000条(应该) 
    void add(long long t,long long w,long long cd)//神奇又好用的链式前向星 
    {
    	ans++;
    	lsqxx[ans].w=w;//结尾处 
    	lsqxx[ans].cd=cd;//这条边的权值 
    	lsqxx[ans].syg=head[t];//上一个起点是t的线的编号 
    	head[t]=ans;//现在这个就是下一个的上一个(啊好乱) 
    }
    bool qg()
    {
    	for(int i=1;i<=n;i++)
    	{
    		sz[i]=1000;//求最短路当然先初始化 
    	}
    	while(p.empty()==false)
    	{
    		wz=p.front();
    		bj[wz]=0;//现在他出去了 
    		p.pop();
    		for(int i=head[wz];i!=0;i=lsqxx[i].syg)
    		{
    			if(sz[lsqxx[i].w]>sz[wz]+lsqxx[i].cd)//松弛操作
    			{
    				sz[lsqxx[i].w]=sz[wz]+lsqxx[i].cd; 
    				if(bj[lsqxx[i].w]==0)//如果已经在队列里面了,再加进去就是浪费时间了 
    				{
    					bj[lsqxx[i].w]=1; 
    					cs[lsqxx[i].w]++;//进队列的次数增加了 
    					if(cs[lsqxx[i].w]==n)//在SPFA中,一个点进入队列n次就一定有负环 
    					{
    						return false;
    					}
    					p.push(lsqxx[i].w);
    				}
    			}
    		}
    	}
    	return true;
    }
    int main()
    {
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)//0号点是超级原点 
    	{
    		add(0,i,0);
    	}
    	for(int i=0;i<m;i++) 
    	{
    		cin>>a>>b>>c;//输入条件 
    		add(b,a,c);//根据条件建图 
    	}
    	p.push(0);
    	if(qg()==false)//有负环,没有解的 
    	{
    		cout<<"NO"<<endl;
    	}else
    	{
    		for(int i=1;i<=n;i++)//sz[i]是最短路,这是一种可行情况 
    		{
    			cout<<sz[i]<<" ";
    		}
    		cout<<endl;
    	}
    	return 0;
    }
  • 相关阅读:
    ASIHTTPRequest类库简介和使用说明
    UIDatePickerView实现时间滚动轮播效果
    UIPickerView选择控件实现选择轮播效果(转轮效果)
    懒加载三大优势
    UIView的自适应高度 (图像,文字)
    正则表达式校验yyyymmdd
    Java时间日期格式转换 转自:http://www.cnblogs.com/edwardlauxh/archive/2010/03/21/1918615.html
    java.sql.SQLException: ORA-00911: 无效字符 解决方法 引自: http://blog.csdn.net/yangzhijun_cau/article/details/6064956
    跨域访问
    maven + eclipse + tomcat热部署 引自:http://jingpin.jikexueyuan.com/article/23068.html
  • 原文地址:https://www.cnblogs.com/lichangjian/p/13399664.html
Copyright © 2011-2022 走看看