題目鏈接 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;
}