问题描述
给出一个n*m的整数矩阵F。
求两个数列A和B,数列A有n个元素:A1,A2,A3...An 。数列B有m个元素:B1,B2,B3.....Bm,
使得对矩阵中的每个数进行下面的计算之后的结果在[X,Y]之间:
计算操作为:A[i] * F[i][j] / B[j]。
输入格式
第一行, 一个整数T(T<=5),表示有T组测试数据
对于每组测试数据:
第一行,四个整数n,m,X,Y
接下来一个n*m的矩阵,数字间以空格做间隔
输出格式
共T行,每行对应一组测试数据的结果:
若能找到满足条件的数列A和B,输出“YES”,否则输出“NO”
样例输入 1
2
3 3 1 6
2 3 4
8 2 6
5 2 9
3 5 8 9
13 12 4 7 5
10 10 13 9 13
3 15 4 5 11
样例输出 1
YES
NO
样例输入 2
2
4 7 2 4
11 10 8 15 8 1 12
8 2 2 8 13 12 2
6 10 9 7 11 10 1
5 9 9 11 7 10 9
3 10 1 11
18 8 18 14 12 6 7 17 15 12
14 3 4 17 8 1 19 4 6 1
8 20 4 7 19 19 6 15 6 16
样例输出 2
NO
YES
提示
1<=N、M<=400,1<=X<=Y<=20000
给出的矩阵中每个数字都是1000以内的正整数。
思路:差分约束。由题意可知,对于矩阵中的每个元素要满足的条件是:
X <= a[i] * F[i][j] / b[j] <= Y ,
这样我们就可以得到下面的两个式子:X*b[j] <= a[i] * F[i][j] 和 a[i] * F[i][j] <= Y*b[j] ,
因为差分约束中dis[]前面没有系数,为了把系数取消掉,我们可以用对式子两遍取对数,
就可以得到:
log(b[j]) - log( a[i] ) <= log( F[i][j] / X) ,
log(a[i]) - log( b[j] ) <= -log( F[i][j] / Y)
同理可以得到两个式子,最后用spfa判负环就可以得出答案了。
N为所有点的个数
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
const int inf=2e9;
const int maxn=700000;
int END[maxn],NEXT[maxn],last[1005],cnt[1005],n,m,Tot;
bool flag[1005];
double X,Y,K,dis[1005],len[maxn];
void insert(int a,int b,double v){
Tot++;
END[Tot]=b;
len[Tot]=v;
NEXT[Tot]=last[a];
last[a]=Tot;
}
bool spfa(){
int i,a,b,t;
for(i=1;i<=n+m;i++){
dis[i]=inf;
cnt[i]=0;
flag[i]=false;
}
dis[1]=0;
queue<int>q;
q.push(1);
flag[1]=true;
cnt[1]++;
while(!q.empty()){
a=q.front();
q.pop();
flag[a]=false;
t=last[a];
while(t){
b=END[t];
if(dis[b]>dis[a]+len[t]){
dis[b]=dis[a]+len[t];
if(!flag[b]){
cnt[b]++;
if(cnt[b]>n+m)return false;
flag[b]=true;
q.push(b);
}
}
t=NEXT[t];
}
}
return true;
}
int main(){
int T,i,j;
scanf("%d",&T);
while(T--){
Tot=0;
memset(last,0,sizeof(last));
scanf("%d%d%lf%lf",&n,&m,&X,&Y);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++){
scanf("%lf",&K);
insert(i,n+j,log(K/X));
insert(n+j,i,-log(K/Y));
}
if(spfa())puts("YES");
else puts("NO");
}
}