题目链接 HDU3666
【题意】给你一个N*M的矩阵,求两列数a1,a2,a3...an 和 b1,b2.....bm使得对矩阵中的每个数进行下面的操作之后的值在[L,U]之间,操作为:a[i] * m[i][j] / b[j]。 N,M<=400
【分析】第一题差分约束,学习了链式前向星存图(比vector快很多这题可以快200ms+)
差分约束的描述 (注意:查分约束对于每个约束 u-v<=c 可以建v->u花费为c的边,<=spfa求最小值而实际问题的最大值,>=spfa求最大值而实际问题的最小值,如果是<或者>则必须通过加减一来变成<=或者>=)
由题意可知,对于矩阵中的每个元素要满足的条件是:L <= a[i] * m[i][j] / b[j] <= U ,这样我们就可以得到下面的两个式子:L*b[j] <= a[i] * m[i][j] 和 a[i] * m[i][j] <= U*b[j] ,因为差分约束中dis[]前面没有系数,为了把系数取消掉,我们可以用对式子两边取对数,就可以得到:log(b[j]) - log( a[i] ) <= log(U/m[i][j]) ,同理可以得到另外一个:log(b[j]) - log(a[i]) <= -log(L/m[i][j])最后用spfa判负环就可以得出答案了
注意:
判断有无解(负环)的时候,如果用spfa,不能用入队次数大于N来判断,会超时。
有如下两种比较可靠的方法(一般情况下)
1:某个点入队次数大于sqrt(N)的时候
2:所有入队次数大于T * (N + M),其中T一般取2
【AC CODE】484ms
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
using namespace std;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long LL;
#define MAXN 810
struct Edge{
int next,to;
double cost;
Edge(int a = 0,int b = 0, double c = 0){next = a, to = b, cost = c;}
}edge[MAXN*MAXN];
int head[MAXN], cnt[MAXN], tol, n, m;
double dis[MAXN];
bool inq[MAXN];
void add_edge(int from, int to, double cost)
{
edge[tol] = Edge(head[from],to,cost);
head[from] = tol++;
}
bool spfa(int s)
{
queue<int> q;
int nn = 2*(n+m), cnt = 0;
rep(i,0,n+m) dis[i] = INF;
clc(inq,0);
inq[s] = true;
dis[s] = 0;
q.push(s);
while(!q.empty())
{
int u = q.front();q.pop();
inq[u] = false;
for(int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].to;
double cost = edge[i].cost;
if(dis[u] < INF && dis[v] > dis[u]+cost)
{
dis[v] = dis[u]+cost;
if(!inq[v])
{
inq[v] = true;
q.push(v);
if(++cnt > nn) return false;
}
}
}
}
return true;
}
int main()
{
#ifdef SHY
freopen("e:\\1.txt","r",stdin);
#endif
double l,u;
while(~scanf("%d %d %lf %lf%*c", &n, &m, &l, &u))
{
int a;
tol = 0;
clc(head,-1);
rep(i,0,n)
{
rep(j,0,m)
{
scanf("%d%*c", &a);
add_edge(i,j+n,-log(l/a));
add_edge(j+n,i,log(u/a));
}
}
if(spfa(0)) puts("YES");
else puts("NO");
}
return 0;
}