【差分約束】

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


 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章