【差分约束】

题目链接 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;
}


 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值