差分约束 矩阵问题

本文探讨了一种矩阵问题,即寻找两个数列A和B,通过对矩阵中的每个数进行特定计算并确保结果位于给定区间内来解决该问题。采用差分约束系统的方法,并通过取对数的方式简化计算过程,最终利用SPFA算法判断是否存在满足条件的数列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D  矩阵问题
时间限制 : - MS   空间限制 : 165536 KB  
评测说明 : 时限:3000ms
问题描述
   给出一个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<cstdio>      
#include<iostream>    
#include<cstring>      
#include<queue>      
#include<vector>       
#include<cmath> 
  
#define LL int      
#define CLEAR(XXX) memset((XXX),0,sizeof(XXX))    
using namespace std;     
     
const LL inf=2e9;      
const int maxn=1005,maxm=700005;      

int n,m,x,y;         
struct Edge{      
    int from,to;
	double w;      
    Edge(int from,int to,double  w):from(from),to(to),w(w){}    
};        
struct SPFA{      
    int  n,m;      
    vector<Edge> edge;      
    int last[maxm],Next[maxm];      
    double dist[maxn];      
    int cnt[maxn];      
    bool vis[maxn];      
    void init(int n){      
        this->n = n;      
        m=0;      
        CLEAR(last);  CLEAR(Next);      
        edge.clear();      
        edge.push_back(Edge(0,0,0));      
    }      
    void add_edge(int from,int to,double dist){      
        edge.push_back(Edge(from,to,dist));      
        m=edge.size()-1;      
        Next[m]=last[from];      
        last[from]=m;      
    }      
    bool solve(int s){      
        int i;      
        CLEAR(vis); CLEAR(cnt);        
        for(i=1;i<=n;i++) dist[i]=inf;      
        dist[s]=0;    
        vis[s]=true;cnt[s]++;      
        queue <int> q;      
        q.push(s);      
        while(!q.empty()){      
            int x=q.front();      
            q.pop();vis[x]=false;  //及时修改标记     
            for(i=last[x];i;i=Next[i]){      
                Edge& e=edge[i];      
                if(dist[e.from]+e.w<dist[e.to]){      
                    dist[e.to]=dist[e.from]+e.w;      
                    if(!vis[e.to]){      
                        cnt[e.to]++;  //统计入队次数,判断负权回路     
                        if(cnt[e.to]==n+1)return false;      
                        q.push(e.to) ;      
                        vis[e.to]=true;      
                    }      
                }       
            }      
        }      
        return true;      
    }       
};    
SPFA solver  ; 
int main(){      
	int T,i,j;
	double t;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d%d",&n,&m,&x,&y);
		solver.init(n+m);
		for(i=1;i<=n;i++)
        	for(j=1;j<=m;j++){
	        	scanf("%lf",&t);
            	solver.add_edge(i,n+j,log(t/x));
            	solver.add_edge(n+j,i,-log(t/y));
          }
        if(solver.solve(1)) puts("YES");
        else puts("NO");
    }
    return 0;
}      


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值