zoukankan      html  css  js  c++  java
  • ZOJ-4028 LIS (差分约束)

    题目链接:ZOJ-4028 LIS

    题意

    有一个长度为 (n (1le n le 10^5)) 的序列 (a),对于所有的 (iin [1,n]) ,已知 (f_i, l_i, r_i) ,代表序列 (a) 中末尾元素为 (a_i) 的最长上升子序列长度为 (f_i) ,且 (l_i le a_i le r_i) ,求序列 (a)


    思路

    找出 (a_i) 的所有约束条件:

    1. (i')(i) 前面满足 (f_{i'}=f_i) 的离 (i) 最近的位置,(a_i) 的值需要满足 (a_i le a_{i'}) ;
    2. (i'')(i) 前面满足 (f_{i''}+1=f_i) 的离 (i) 最近的位置,(a_i) 的值需要满足 (a_i>a_{i''}) ;
    3. (l_ile a_ile r_i)

    知道了每个 (a_i) 的约束条件,可以用差分约束求解(用 (e(u,v,w)) 代表从 (u)(v) 权值为 (w) 的有向边):

    1. (a_ile a_{i'}) 可变形为 (a_ile a_{i'}+0) ,连边 (e(i',i,0)) ;
    2. (a_i>a_{i''}) 可变形为 (a_{i''}le a_i-1) ,连边 (e(i,i'',-1)) ;
    3. 新增一个源点 (s),令 (a_s=0)(l_ile a_ile r_i) 可变形为 (a_sle a_i-l_i, a_ile a_s+r_i) ,连边 (e(i,s,-l_i), e(s,i,r_i))

    建完图跑差分约束即可。


    代码实现

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using std::queue;
    typedef long long LL;
    const int maxn = 100010;
    int head[maxn], tot;
    LL dist[maxn];
    int f[maxn], last[maxn];
    bool inq[maxn];
    struct Edge{
        int to, nex, val;
    } edge[maxn<<2];
    void add_edge(int u, int v, int w) {
        edge[++tot].nex = head[u];
        edge[tot].to = v;
        edge[tot].val = w;
        head[u] = tot;
    }
    void bfs_spfa(int s, int n) {
        memset(inq, 0, sizeof(bool) * (n+5));
        memset(dist, 0x3f, sizeof(LL) * (n+5));
        queue<int> que;
        que.push(s);
        inq[s] = true;
        dist[s] = 0;
        while (!que.empty()) {
            int u = que.front();
            que.pop();
            inq[u] = false;
            for (int i = head[u]; i; i = edge[i].nex) {
                int v = edge[i].to;
                if (dist[v] > dist[u] + edge[i].val) {
                    dist[v] = dist[u] + edge[i].val;
                    if (!inq[v]) {
                        inq[v] = true;
                        que.push(v);
                    }
                }
            }
        }
        for (int i = 1; i <= n; i++) printf("%lld%c", dist[i], " 
    "[i==n]);
    }
    
    int main() {
        int T, n;
        scanf("%d", &T);
        while (T--) {
            scanf("%d", &n);
            memset(last, 0, sizeof(int) * (n+5));
            memset(head, 0, sizeof(int) * (n+5));
            tot = 0;
            for (int i = 1; i <= n; i++) {
                scanf("%d", &f[i]);
                if (last[f[i]]) add_edge(last[f[i]], i, 0);
                if (f[i] > 1) add_edge(i, last[f[i]-1], -1);
                last[f[i]] = i;
            }
            for (int i = 1, l, r; i <= n; i++) {
                scanf("%d %d", &l, &r);
                add_edge(0, i, r);
                add_edge(i, 0, -l);
            }
            bfs_spfa(0, n);
        }
        return 0;
    }
    
  • 相关阅读:
    32位和64位系统区别及int字节数
    进程的三种状态及转换
    已知二叉树的前序/后序遍历和中序遍历,求后序/前序遍历
    一步一步写算法
    Ubuntu中APache+mod_pyhon
    JAVA SOCKET
    TCP连接 断开
    mfc 创建一个C++ 类
    mfc 类的析构函数
    mfc 类对象的引用
  • 原文地址:https://www.cnblogs.com/kangkang-/p/13512581.html
Copyright © 2011-2022 走看看