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 }