感觉搞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;
}