poj1201&&hdu1384 Intervals(差分约束)


http://poj.org/problem?id=1201

题意:m个约束条件,a b c表示在[a,b]区间内整数至少有c个,求满足这样要求的最短序列长度。


思路:因为序列是连续的,所以和上一道题一样同样可以转化为序列和之差,那么S[b]-S[a-1]>=c这个条件就出来了,根据性质也知道是求最长路。不过这题还有个更重要的隐藏条件1 <= ci <= bi - ai+1,这说明区间长度至少为0。刚开始不知道怎么用,这里用了极限化的思想。我们知道本题一个约束条件要想进入系统,必须和最关键的约束条件格式一致,都是序列和>=某一个数。而这里要想利用这个条件,那就满足条件,而长度为0的序列和相减没有意义,所以最小变成了长度为1的序列和相减。自此,这个条件就用完了= =,有推出什么结论么?关键的来了,我们带上权值分析,长度为1的序列和相减就是一个元素,而一个元素只有两种可能,存在或不存在,存在为1不存在为0,这样就推出来了,0<=S[i]-S[i-1]<=1。转化就变成S[i]-S[i-1]>=0和S[i-1]-S[i]>=-1,将这两个条件当做边加入系统,注意这是所有约束条件内相邻序列和都有的性质。


真尼玛坎坷,我记得初高中做数学题就经常漏条件,当时还不服,现在见识到了隐藏条件的威力了。。准确的说,隐藏条件反映了一个序列的性质,没条件就相当于没性质,那这个序列就是乱序少了约束条件。多了一点感悟。。


由于求的是满足条件的最短序列长度,所以源点就是所有约束条件的最小值,无需最小源点。另外这题是用栈优化的,之前代码懒得改了。


#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>
#include <stack>

using namespace std;

typedef long long LL;

const int N = 50005;
const int INF = 0x3f3f3f3f;

int dis[N], head[N], cnt, L, R;
bool instack[N];

struct node
{
    int v, w, next;
}edge[N*4];

void add(int u, int v, int w)
{
    edge[cnt] = (struct node){v, w, head[u]};
    head[u] = cnt++;
}

int spfa()
{
    memset(instack, false, sizeof(instack));
    memset(dis, -INF, sizeof(dis));
    stack<int>sta;
    dis[L] = 0;
    instack[L] = true;
    sta.push(L);
    while(!sta.empty())
    {
        int u = sta.top();
        sta.pop();
        instack[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if(dis[u]+edge[i].w>dis[v])
            {
                dis[v] = dis[u]+edge[i].w;
                if(!instack[v])
                {
                    instack[v] = true;
                    sta.push(v);
                }
            }
        }
    }
    return dis[R];
}

int main()
{
  //  freopen("in.txt", "r", stdin);
    int m, a, b, c;
    while(~scanf("%d", &m))
    {
        cnt = R = 0;
        L = INF;
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%d", &a, &b, &c);
            L = min(L, a-1);
            R = max(R, b);
            add(a-1, b, c);
        }
        for(int i = L; i <= R; i++)
        {
            add(i-1, i, 0);
            add(i, i-1, -1);
        }
        printf("%d\n", spfa());
    }
    return 0;
}



http://acm.hdu.edu.cn/showproblem.php?pid=1384

杭电上同样的代码用栈优化的会超时,只需改成队列就可以。


#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>
#include <stack>

using namespace std;

typedef long long LL;

const int N = 50005;
const int INF = 0x3f3f3f3f;

int dis[N], head[N], cnt, L, R;
bool instack[N];

struct node
{
    int v, w, next;
}edge[N*4];

void add(int u, int v, int w)
{
    edge[cnt] = (struct node){v, w, head[u]};
    head[u] = cnt++;
}

int spfa()
{
    memset(instack, false, sizeof(instack));
    queue<int>sta;
    memset(dis, -INF, sizeof(dis));
    dis[L] = 0;
    instack[L] = true;
    sta.push(L);
    while(!sta.empty())
    {
        int u = sta.front();
        sta.pop();
        instack[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if(dis[u]+edge[i].w>dis[v])
            {
                dis[v] = dis[u]+edge[i].w;
                if(!instack[v])
                {
                    instack[v] = true;
                    sta.push(v);
                }
            }
        }
    }
    return dis[R];
}

int main()
{
  //  freopen("in.txt", "r", stdin);
    int m, a, b, c;
    while(~scanf("%d", &m))
    {
        cnt = R = 0;
        L = INF;
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%d", &a, &b, &c);
            L = min(L, a-1);
            R = max(R, b);
            add(a-1, b, c);
        }
        for(int i = L; i <= R; i++)
        {
            add(i-1, i, 0);
            add(i, i-1, -1);
        }
        printf("%d\n", spfa());
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值