HDU--3666(差分约束)

2015-01-08 22:48:00

思路:巧妙的建图...

  由: L <= cij * ai / bj <= U,要转化成加减的话全体套上log...

  化为:log(L) <= log(cij) + log(ai) - log(bj) <= log(U)

  --> (1) log(ai) <= log(bj) + log(U) - log(cij)

  --> (2) log(bj) <= log(ai) + log(cij) - log(L)

  对点进行编号,1~N为a1~N,N+1~N+M为b1~M,那么上述(1)(2)约束就化为:

    (1) S(i) <= S(N + j) + log(U) - log(cij)

    (2) S(N + j) <= S(i) + log(cij) - log(L)

  然后判断是否有负环即可,用dfs版spfa不会超时~

  (至于队列版spfa超时,有些人把判圈的范围变小(比如弄成sqrt(总数)),这种写法正确性尚待考究...有用正确性来换时间的嫌疑 -。-)

  关于写法,有个细节:对每一个点进行spfa,用vis数组判断是否已经判断过这个点(防超时),一旦遍历到某个仍在栈中的点说明有负圈。

  (具体可参考2006年国家集训队姜碧野的论文。)

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 typedef pair<int,int> pii;
20 const int INF = (1 << 30) - 1;
21 const int maxn = 1000;
22 
23 int N,M,L,U,g;
24 int first[maxn],ecnt;
25 int inq[maxn],cnt[maxn],vis[maxn];
26 double dis[maxn];
27 
28 struct edge{
29     int v,next;
30     double cost;
31 }e[maxn * maxn * 2];
32 
33 inline void Add_edge(int u,int v,double c){
34     e[++ecnt].next = first[u];
35     e[ecnt].v = v;
36     e[ecnt].cost = c;
37     first[u] = ecnt;
38 }
39 
40 bool Spfa(int p){
41     inq[p] = vis[p] = 1;
42     for(int i = first[p]; ~i; i = e[i].next){
43         int v = e[i].v;
44         if(dis[v] > dis[p] + e[i].cost){
45             dis[v] = dis[p] + e[i].cost;
46             if(inq[v] || !Spfa(v))
47                 return false;
48         }
49     }
50     inq[p] = 0;
51     return true;
52 }
53 
54 bool Solve(){
55     memset(inq,0,sizeof(inq));
56     memset(vis,0,sizeof(vis));
57     memset(dis,0,sizeof(dis));
58     for(int i = N + M; i >= 0; --i) if(!vis[i]){
59         if(Spfa(i) == false){
60             return false;
61         }
62     }
63     return true;
64 }
65 
66 int main(){
67     while(scanf("%d%d%d%d",&N,&M,&L,&U) != EOF){
68         memset(first,-1,sizeof(first));
69         ecnt = 0;
70         double tl = log(1.0 * L);
71         double tu = log(1.0 * U);
72         for(int i = 1; i <= N; ++i){
73             for(int j = 1; j <= M; ++j){
74                 scanf("%d",&g);
75                 double tg = log(1.0 * g);
76                 Add_edge(i,N + j,tg - tl);
77                 Add_edge(N + j,i,tu - tg);
78             }
79         }
80         for(int i = N + M; i >= 1; --i)
81             Add_edge(0,i,0);
82         if(Solve()) printf("YES\n");
83         else printf("NO\n");
84     }
85     return 0;
86 }

 顺便尝试了一下论文中提到的贪心初始流写法,发现这么写效率虽然没什么差别,但是可以省去vis数组,还是有效果的!(不用预流处理且不加vis数组的话会超时。)

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 typedef pair<int,int> pii;
20 const int INF = (1 << 30) - 1;
21 const int maxn = 1000;
22 
23 int N,M,L,U,g;
24 int first[maxn],ecnt;
25 int inq[maxn],cnt[maxn];
26 double dis[maxn];
27 
28 struct edge{
29     int v,next;
30     double cost;
31 }e[maxn * maxn * 2];
32 
33 inline void Add_edge(int u,int v,double c){
34     e[++ecnt].next = first[u];
35     e[ecnt].v = v;
36     e[ecnt].cost = c;
37     first[u] = ecnt;
38 }
39 
40 bool Spfa(int p){
41     inq[p] = 1;
42     for(int i = first[p]; ~i; i = e[i].next){
43         int v = e[i].v;
44         if(dis[v] > dis[p] + e[i].cost){
45             dis[v] = dis[p] + e[i].cost;
46             if(inq[v] || !Spfa(v))
47                 return false;
48         }
49     }
50     inq[p] = 0;
51     return true;
52 }
53 
54 bool Spfa_init(int p){
55     for(int i = first[p]; ~i; i = e[i].next){
56         int v = e[i].v;
57         if(dis[v] > dis[p] + e[i].cost){
58             dis[v] = dis[p] + e[i].cost;
59             Spfa_init(v);
60             return true;
61         }
62     }
63     return false;
64 }        
65 
66 bool Solve(){
67     memset(inq,0,sizeof(inq));
68     memset(dis,0,sizeof(dis));
69     int cur = 0;
70     while(Spfa_init(cur)) cur++;  //预流
71     for(int i = N + M; i >= 0; --i)
72         if(Spfa(i) == false)
73             return false;
74     return true;
75 }
76 
77 int main(){
78     while(scanf("%d%d%d%d",&N,&M,&L,&U) != EOF){
79         memset(first,-1,sizeof(first));
80         ecnt = 0;
81         double tl = log(1.0 * L);
82         double tu = log(1.0 * U);
83         for(int i = 1; i <= N; ++i){
84             for(int j = 1; j <= M; ++j){
85                 scanf("%d",&g);
86                 double tg = log(1.0 * g);
87                 Add_edge(i,N + j,tg - tl);
88                 Add_edge(N + j,i,tu - tg);
89             }
90         }
91         for(int i = N + M; i >= 1; --i)
92             Add_edge(0,i,0);
93         if(Solve()) printf("YES\n");
94         else printf("NO\n");
95     }
96     return 0;
97 }
View Code

 

转载于:https://www.cnblogs.com/naturepengchen/articles/4212224.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值