题目大意:
题目链接:http://poj.org/problem?id=1201
从0∼500000\sim500000∼50000中选择尽量少的数字使得nnn组形如“xxx到yyy中选择的数字不少于ckc_kck个”的要求全部满足。
思路:
设sis_isi表示从0∼i0\sim i0∼i中选择的数字个数。那么对于任意一个要求x,y,ckx,y,c_kx,y,ck,都需要满足sy+1−sx≥cks_{y+1}-s_x\geq c_ksy+1−sx≥ck,与差分约束十分相像。于是从yyy连向xxx一条长度为ckc_kck的边。
同时我们对于数字iii,肯定是要么选择,要么不选。所以本题中还有两个隐含条件sk−sk−1≥0s_k-s_{k-1}\geq 0sk−sk−1≥0,sk−sk−1≤1s_k-s_{k-1}\leq 1sk−sk−1≤1。
对于条件1,可以直接从k−1k-1k−1向kkk连一条长度为0的边,对于条件2,变形得sk−sk−1≥−1s_k-s_k-1\geq -1sk−sk−1≥−1,从kkk向k−1k-1k−1连一条长度为-1的边。
由于不等式符号全部是大于,所以需要跑最长路。很明显图中是不含正环的,所以可以不用判正环。以-1为源点跑spfaspfaspfa即可。答案即为dis[50000]dis[50000]dis[50000]。
但是下标是不可以用负数的。所以可以把所有点的下标+1,变成“从1∼500011\sim500011∼50001”中选择,从0开始跑spfaspfaspfa,答案就是dis[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;
}
















