hdu 3666 差分约束系统

这也是我第一次碰到差分约束的题。。。

好吧。。。赶紧学一下吧。。

差分约束系统:

假设有这样一组不等式:

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这题:

题目大意是:
给你一个N*M的矩阵,求两列数a1,a2,a3...an 和 b1,b2.....bm使得对矩阵中的每个数进行下面的操作之后的值在[L,U]之间,操作为:a[i] * map[i][j] / b[j]。  N,M<=400

首先   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。。。

好吧。。。到这儿也差不多了,我就直接上代码了。。。

View Code
 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,生命不休。。学习不止。。。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值