题目大意:
题目链接:http://poj.org/problem?id=1201
从
0
∼
50000
0\sim50000
0∼50000中选择尽量少的数字使得
n
n
n组形如“
x
x
x到
y
y
y中选择的数字不少于
c
k
c_k
ck个”的要求全部满足。
思路:
设
s
i
s_i
si表示从
0
∼
i
0\sim i
0∼i中选择的数字个数。那么对于任意一个要求
x
,
y
,
c
k
x,y,c_k
x,y,ck,都需要满足
s
y
+
1
−
s
x
≥
c
k
s_{y+1}-s_x\geq c_k
sy+1−sx≥ck,与差分约束十分相像。于是从
y
y
y连向
x
x
x一条长度为
c
k
c_k
ck的边。
同时我们对于数字
i
i
i,肯定是要么选择,要么不选。所以本题中还有两个隐含条件
s
k
−
s
k
−
1
≥
0
s_k-s_{k-1}\geq 0
sk−sk−1≥0,
s
k
−
s
k
−
1
≤
1
s_k-s_{k-1}\leq 1
sk−sk−1≤1。
对于条件1,可以直接从
k
−
1
k-1
k−1向
k
k
k连一条长度为0的边,对于条件2,变形得
s
k
−
s
k
−
1
≥
−
1
s_k-s_k-1\geq -1
sk−sk−1≥−1,从
k
k
k向
k
−
1
k-1
k−1连一条长度为-1的边。
由于不等式符号全部是大于,所以需要跑最长路。很明显图中是不含正环的,所以可以不用判正环。以-1为源点跑
s
p
f
a
spfa
spfa即可。答案即为
d
i
s
[
50000
]
dis[50000]
dis[50000]。
但是下标是不可以用负数的。所以可以把所有点的下标+1,变成“从
1
∼
50001
1\sim50001
1∼50001”中选择,从0开始跑
s
p
f
a
spfa
spfa,答案就是
d
i
s
[
50001
]
dis[50001]
dis[50001]。
代码:
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N=50010;
int n,x,y,z,tot,head[N],dis[N];
bool vis[N];
struct edge
{
int next,to,dis;
}e[N*3];
void add(int from,int to,int dis)
{
e[++tot].to=to;
e[tot].dis=dis;
e[tot].next=head[from];
head[from]=tot;
}
void spfa()
{
memset(dis,0xcf,sizeof(dis));
queue<int> q;
q.push(0);
dis[0]=0;
vis[0]=1;
while (q.size())
{
int u=q.front(),v;
q.pop();
vis[u]=0;
for (int i=head[u];~i;i=e[i].next)
{
v=e[i].to;
if (dis[v]<dis[u]+e[i].dis)
{
dis[v]=dis[u]+e[i].dis;
if (!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x+1,y+2,z);
}
for (int i=1;i<=50001;i++)
add(i-1,i,0),add(i,i-1,-1);
spfa();
printf("%d\n",dis[50001]);
return 0;
}