POJ 1201 Interval (Spfa 差分约束系统)

POJ 1201
ZOJ 1508 Interval

题目大意

有一个序列,题目用n个整数组合 [aibici] 来描述它, [aibici] 表示在该序列中处于 [aibi] 这个区间的整数至少有 ci 个。如果存在这样的序列,请求出满足题目要求的最短的序列长度是多少。如果不存在则输出 -1。 (0aibi50000,1cibiai+1)

分析

关于一个区间里和的数目的问题很容易联想到用 sum[i] 表示从起点到i的总的和,那么 sum[b]sum[a1] 就表示区间 [a,b] 的和了。

再根据题目的约束条件 sum[bi]sum[ai1]ci ,

还需要注意的地方是除了上面这个约束条件,题目还隐含的约束条件就是 0sum[i]sum[i1]1

转化之后就是

sum[bi]sum[ai1]cisum[i]sum[i1]0sum[i1]sum[i]1

由于题目中 ai 最小是0,所以在输入的时候把 a,b 都加上1避免数组-1越界

这样就变成了差分约束系统的问题了。
bellman-ford的松弛操作的目的是为了让点的距离满足 dis[v]<=dis[u]+w[u][v] 这一约束条件,转化一下就变成了 dis[u]dis[v]w[u][v] ,这样就和常见差分约束问题约束条件的形式 (abc) 一样了,上面的 sum 数组也就对应与图中的距离。

差分约束系统

这里简单写一下差分约束系统的思路,约束条件 (abc) 意思是a至少要比b大c.

而在路径问题中的边(x指向y,权值为w)的含义是y的距离最多比x大w.

“y的距离最多比x大w” (dist[y]dist[x]+w) n “x至少要比y大-w” (dist[x]dist[y]w)

所以约束条件 (abc) :a至少要比b大c等价于 “b的距离最多比a大-c”也就是a指向b权值为-c

可以通过大于的传递性得到一个变量 Xi 比另一个变量 Xj 至少大多少也就是 max(XiXj)=max(c) ,但路径上的权值加起来是 (c) 要求 (c) 的最大值就是求路径 (c) 的最小值,最短路算法计算的过程中 Xi 作为源点。

代码

/*
有向图
前向星存边
这道题没有成环的情况
*/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
using namespace std;
const int INF=0x7fffffff;//无穷大
struct Edge
{
      int to;
      int next;
      int w;
}edge[500005];
int edgecount;
int head[50005];
int dis[50005];
bool vis[50005];
void Init_edge()
{
      memset(head,-1,sizeof(head));
      edgecount=0;
}
void Add_Edge(int u,int v,int w)
{
     edge[++edgecount].to=v;
     edge[edgecount].w=w;
     edge[edgecount].next=head[u];
     head[u]=edgecount;
}
void Spfa(int n)
{
      memset(vis,0,sizeof(vis));
      for(int i=0;i<=n;i++)dis[i]=INF;
      dis[n]=0;
      vis[n]=1;
      queue<int> Q;
      Q.push(n);
      int cnt=1;
      while(!Q.empty())
      {
            int u=Q.front();
            Q.pop();
            for(int k=head[u];k!=-1;k=edge[k].next)
            {
                  int t=dis[u]+edge[k].w;
                  if(dis[edge[k].to]>t)
                  {
                      dis[edge[k].to]=t;
                      if(vis[edge[k].to]==1)continue;
                      Q.push(edge[k].to);
                      vis[edge[k].to]=1;
                  }
            }
            vis[u]=0;
      }
      cout<<-dis[0]<<endl;
}
int main()
{
    int n;
    int a,b,c;
    int maxn;
    while(scanf("%d",&n)!=EOF && n)
    {
          Init_edge();
          maxn=0;
          for(int i=1;i<=n;i++)
          {
                scanf("%d%d%d",&a,&b,&c);
                a++;b++;
                maxn=max(b,maxn);
                Add_Edge(b,a-1,-c);
          }
          for(int i=1;i<=maxn;i++)
          {
                Add_Edge(i,i-1,0);
                Add_Edge(i-1,i,1);
          }
          Spfa(maxn);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值