这也是我第一次碰到差分约束的题。。。
好吧。。。赶紧学一下吧。。
差分约束系统:
假设有这样一组不等式:
x1-x2<=0;
x1-x5<=-1;
x2-x5<=1;
x3-x1<=5;
x4-x1<=4;
x4-x3<=-1;
。。。。
每个不等式都是有两个未知数的差小于等于某个常数(大于等于也可以,只要两边同乘以-1就可以了),这样的不等式组就称作差分约束系统(这个不等式组要么无解,要么有无数组解,因为若(x1,x2,x3....)是一组解,那么(x1+k,x2+k,x3+k,...)也必然是一组解);
差分约束系统与最短路径的关系:
差分约束系统的求解可以利用单源最短路径中的三角不等式,即对于边<u,v>都有dist[v]<=dist[u]+edge[u][v];
于是上面的不等式就可以化为dist[v]-dist[u]<=edge[u][v],从而就把一个差分约束系统转化为了一个图;
<构造方法>
1、不等式中的每个未知数都对应图中的一个顶点;
2、把所有的不等式都化为图中的一条边,对于不等式xi-xj<=c,就可以化为边<vi,vj>,其中权值为c;
于是,对于hdu 3666这题:
首先 L<= c[i][j] *ai / bj <= u , 我们遇到的都是减法的差分约束,那么我们取log就可以了,两边同除以 c[i][j] , 然后取 log 锝 ,log(l) - log(c[i][j]) <= log(a) - log(b) <= log(u) - log(c[i][j]) ; 然后 SPFA判断是否有负环,如果存在,则说明无解。
判断有无解(负环)的时候,如果用spfa,不能用入队次数大于N来判断,会超时。
有如下两种比较可靠的方法(一般情况下)
1:某个点入队次数大于sqrt(N)的时候
2:所有入队次数大于T * (N + M),其中T一般取2。。。
好吧。。。到这儿也差不多了,我就直接上代码了。。。
1 #include<iostream> 2 #include<queue> 3 #include<cstring> 4 #include<cmath> 5 const int N=1000; 6 const double inf=1e10; 7 using namespace std; 8 9 double dist[N]; 10 int head[N]; 11 int visited[N]; 12 int _count[N];//用来记录同一个点入队列的次数 13 int n,m,cnt; 14 15 struct point { 16 int v,next; 17 double w;//终点、权值、以及指向下一条边 18 }edge[N*N]; 19 20 void CreateMap(int u ,int v,double w){ 21 edge[cnt].v=v; 22 edge[cnt].w=w; 23 edge[cnt].next=head[u]; 24 head[u]=cnt++; 25 } 26 27 int SPFA(){ 28 memset(visited,0,sizeof(visited)); 29 memset(_count,0,sizeof(_count)); 30 for(int i=1;i<=n+m;i++){ 31 dist[i]=inf; 32 } 33 dist[1]=0; 34 visited[1]=1; 35 _count[1]++; 36 queue<int>Q; 37 Q.push(1); 38 while(!Q.empty()){ 39 int tmp=Q.front(); 40 Q.pop(); 41 visited[tmp]=0; 42 for(int i=head[tmp];i!=-1;i=edge[i].next){ 43 if(dist[tmp]+edge[i].w<dist[edge[i].v]){ 44 dist[edge[i].v]=dist[tmp]+edge[i].w; 45 if(!visited[edge[i].v]){ 46 Q.push(edge[i].v); 47 visited[edge[i].v]=1; 48 if(++_count[edge[i].v]>(int)sqrt(1.0*(n+m)))//同一个点入队列次数超过sqrt(n+m)次,就说明无解 49 return 0; 50 51 } 52 } 53 } 54 } 55 return 1; 56 } 57 58 59 int main(){ 60 int temp; 61 double l,u; 62 while(scanf("%d%d",&n,&m)!=EOF){ 63 memset(head,-1,sizeof(head)); 64 scanf("%lf%lf",&l,&u); 65 cnt=1; 66 for(int i=1;i<=n;i++){ 67 for(int j=1;j<=m;j++){ 68 scanf("%d",&temp); 69 CreateMap(i,j+n,-log(l*1.0/temp)); 70 CreateMap(j+n,i,log(u*1.0/temp)); 71 } 72 } 73 int flag=SPFA(); 74 if(flag){ 75 printf("YES\n"); 76 }else 77 printf("NO\n"); 78 } 79 return 0; 80 }
好吧,明天继续差分约束系统。。。orz,生命不休。。学习不止。。。