感觉搞acm还是不能心急,昨天看了查分约束系统的论文后就迫不及待地做题,其实算法还没理解透。。。导致一下午的悲剧。。。回寝后冷静下来看了两小时书,今天做题明显感觉好多了。
根据这个题意,不难得出 L <= C[i][j]*a[i]/b[j] <= U的不等式,由于差分约束系统的不等式都是减法形式,对于除法变减法,显然用log函数,于是就得到了
(1) log(b[j]) - log(a[i]) <= -log(L/c[i][j])
(2) log(a[i] - log(b[j]) <= log(U/c[i][j])
这两个不等式,然后就是建图,spfa判负环,果断超时了一次。。。上网一查,如果用传统的判负环方法(判断一个点是否入队超过N次)是要超时的。。。,只需将每个节点的入队上限由N变成sqrt(N)就行,有待研究。。。用vector表示邻接表的spfa1500+ms飘过。。。有点虚。。。
#include<iostream> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> using namespace std; const int maxn = 400 * 400 + 500; const double INF = 1e10; const double eps = 1e-8; int n, m, limit; double c, l, u; struct Edge { int from, to; double dist; Edge(){}; Edge(int a, int b, double c) {from=a, to=b, dist=c;} }; vector<Edge> edges; vector<int> G[maxn]; int cnt[maxn]; double d[maxn]; bool inq[maxn]; void init() { for(int i=0; i<=n+m; i++) G[i].clear(); edges.clear(); } void add(int from, int to, double dist) { Edge e = Edge(from, to, dist); edges.push_back(e); int tmp = edges.size(); G[from].push_back(tmp-1); } bool spfa(int s) { queue<int> q; q.push(s); memset(inq, 0, sizeof(inq)); memset(cnt, 0, sizeof(cnt)); for(int i=0; i<=n+m; i++) d[i] = INF; d[s] = 0; inq[s] = 1; cnt[s] = 1; while(!q.empty()) { int u = q.front(); q.pop(); inq[u] = 0; for(int i=0; i<G[u].size(); i++) { Edge& e = edges[G[u][i]]; if(d[e.to] - (d[u] + e.dist) > eps) { d[e.to] = d[u] + e.dist; if(!inq[e.to]) { q.push(e.to); inq[e.to] = 1; if(++cnt[e.to] > limit) return 1; } } } } return 0; } int main() { while(~scanf("%d%d%lf%lf", &n, &m, &l, &u)) { init(); limit = (int)sqrt(1.0*(n+m)); // limit = n+m; 这个果断超时。。。 for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) { scanf("%lf", &c); add(j+n, i, -log(l/c)); add(i, j+n, log(u/c)); } if(spfa(1)) puts("NO") ; else puts("YES"); } return 0; }