1. 程式人生 > >ZOJ - 4028 LIS 差分約束

ZOJ - 4028 LIS 差分約束

題意:給你每個位置以該位置結尾的最長上升子序列的值,然後給你每個點的取值範圍,求可行的原序列

題解: 我們另 源點 為 (n+1)點

1. 因為對於每個a[i]  滿足 l[i] <= a[i] <= r[i]  所以 轉換為  (n+1) -a[i]  <= -l[i]     a[i] - (n+1) <= r[i]

2. 若i點最長上升子序列為 k  那麼他要比前面為k 的數小於等於 ,pre[k] 表示之前 該值出現的位置 則:a[i] - a[pre[k]] <= 0

3. 若i點最長上升子序列為 k,那麼他要比之前出現 k-1 位置 的數大 即:a[i] - a[pre[k - 1]] >= 1 即:a[pre[k - 1]] - a[i] <= -1

線性約束條件找好,建圖,從原點跑最短路即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e5+10;
typedef long long ll;
#define INF 0x7f7f7f7f
struct node
{
    int to,nex,d;
}e[N*10];
int head[N],vis[N],len,pre[N];
ll dis[N];
int n,l[N],r[N],f[N];
void init()
{
    len=0;
    for(int i=0;i<=n+1;i++)
    {
        head[i]=-1;
        dis[i]=INF;
        vis[i]=0;
        pre[i]=0;
    }
}
void add(int u,int v,int d)
{
    e[len].to=v;
    e[len].d=d;
    e[len].nex=head[u];
    head[u]=len++;
}
void spfa(int s)
{
    int u,to;
    queue<int> q;
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].nex)
        {
            to=e[i].to;
            if(dis[to]>dis[u]+e[i].d)
            {
                dis[to]=dis[u]+e[i].d;
                if(!vis[to]) q.push(to),vis[to]=1;
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);

        for(int i=1;i<=n;i++)scanf("%d",&f[i]);
        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&l[i],&r[i]);
            add(i,n+1,-l[i]);
            add(n+1,i,r[i]);
            add(i,pre[f[i]-1],-1);
            if(pre[f[i]]) add(pre[f[i]],i,0);
            pre[f[i]]=i;
        }
        spfa(n+1);
        for(int i=1;i<=n;i++)printf("%lld%c",dis[i]," \n"[i==n]);
    }
    return 0;
}