题目描述:
Intervals
Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 32163 Accepted: 12444 Description
You are given n closed, integer intervals [ai, bi] and n integers c1, ..., cn.
Write a program that:
reads the number of intervals, their end points andintegers c1, ..., cn from the standard input,
computes the minimal size of a set Z of integers which has at leastci common elements with interval [ai, bi], for each i=1,2,...,n,
rites the answer to the standard output.
Input
The first line of the input contains an integer n (1 <= n <= 50000) -- the number of intervals.
The following n lines describe the intervals. The (i+1)-th line of the input contains three integers ai,
bi and ci separated by single spaces and such that 0 <= ai <= bi <= 50000
and 1 <= ci <= bi - ai+1.
Output
The output contains exactly one integer equal to the minimal size of set Z sharing at
least ci elements with interval [ai, bi], for each i=1,2,...,n.
Sample Input
5 3 7 3 8 10 3 6 8 1 1 3 1 10 11 1Sample Output
6
初次接触差分约束系统的题目,感觉 题解中 的难点有:
①建立差分约束模型(同时明确什么是点,什么是边权等)
②找出隐含关系;
先直接上代码吧:
#define ll long long
using namespace std;
const int N=50000+10;
const int M=50010*4;
int head[N];
int edge[M],ver[M];
int tot;
int v[N];
int Next[M];
int d[N];
void add(int x,int y,int z)
{
edge[++tot]=z;
ver[tot]=y;
Next[tot]=head[x];
head[x]=tot;
}
queue <int> q;
int cnt[M];
int n;
bool spfa(int minn,int maxx)
{
memset(cnt,0,sizeof(cnt));
for(int i=minn;i<=maxx;++i)d[i]=-999999999;
memset(v,0,sizeof(v));
d[minn]=0;//注意起点不再是“1”点
v[minn]=1;
cnt[minn]=0;
q.push(minn);
while(q.size())
{
int x=q.front();
q.pop();
v[x]=0;
for(int i=head[x];i;i=Next[i])
{
int y=ver[i];
int z=edge[i];
if(d[y]<d[x]+z)//求最长路
{
d[y]=d[x]+z;
cnt[y]=cnt[x]+1;
if(cnt[y]>=maxx-minn+1)return 0;//负环判断
if(!v[x])q.push(y),v[y]=1;
}
}
}
return 1;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int minn=999999999;
int maxx=-999999999;
tot=0;
memset(head,0,sizeof(head));
memset(edge,0,sizeof(edge));
int x,y,z;
for(int i=1;i<=n;++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y+1,z);//只有单向边
minn=min(minn,x);//所以,起点只从x中取得
maxx=max(maxx,y+1);//终点只从y+1中取得
}
//下面这个for是题目隐含的不等关系,需要自己看出来;
for(int i=minn;i<maxx;++i)
//这里i小于或者小于等于maxx均可以,i+1点对原点到i的最长路我影响
{
add(i,i+1,0);
add(i+1,i,-1);
}
bool f=spfa(minn,maxx);
if(f)printf("%d\n",d[maxx]);
}
return 0;
}
第二次做:
POJ 1201:区间覆盖差分约束:
核心题意:
题意:有n个如下形式的条件:
ai bi ci,表示在区间[ai, bi]内至少要选择ci个整数点.
问你满足n个条件的情况下,最少需要选多少个点?
理解:
不在题目描述区间中的点对最终结果没有影响,
所有有负节点的可以加上一个较大数使得所有点均为正数。
注意:
隐含条件,这种题目中,所求最长路最短路表示前缀和,所以后面的一定要>=前面的,
且保证增量最多为一。
注意负环判断时的条件:n指的那个量。(应该是节点中的最大值,不区分是否在题目
描述的几个小区间内是否出现)。
因为所有d值均大于等于0,所以只能求最长路,求最短路没法通过0节点将所有点相连接。
AC代码:(还是最长路)
#define LL long long
#define par pair<int,int>
#define INF 0x3f3f3f3f
#define io ios::sync_with_stdio(false)
using namespace std;
const int N=1e5+100;
const int M=2e5+100;
using namespace std;
int head[N];
int Next[M];
int edge[M];
int ver[M];
int tot;
void add(int x,int y,int z)
{
ver[++tot]=y;
Next[tot]=head[x];
edge[tot]=z;
head[x]=tot;
}
int d[N],vis[N],cnt[N];
int spfa(int n)
{
queue<int>q;
while(q.size())q.pop();
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
memset(d,-INF,sizeof(d));
d[1]=0;
vis[1]=1;
cnt[1]=0;
q.push(1);
while(q.size())
{
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i;i=Next[i])
{
int y=ver[i];
if(d[y]<d[x]+edge[i])
{
d[y]=d[x]+edge[i];
cnt[y]=cnt[x]+1;
if(cnt[y]>=n)return 0;
if(vis[y]==0)vis[y]=1,q.push(y);
}
}
}
return 1;
}
int main()
{
io;
cin.tie(0);
cout.tie(0);
int n;
while(cin>>n)
{
memset(head,0,sizeof(head));
memset(Next,0,sizeof(Next));
memset(ver,0,sizeof(ver));
memset(edge,0,sizeof(edge));
tot=1;
int maxx=-1;
for(int i=1;i<=n;++i)
{
int x,y,z;
cin>>x>>y>>z;
x+=1;
y+=1;
if(x>maxx)maxx=x;
if(y>maxx)maxx=y;
add(x-1,y,z);
}
for(int i=2;i<=maxx;++i)add(1,i,0);
for(int i=2;i<=maxx;++i)add(i+1,i,-1);
for(int i=3;i<=maxx;++i)add(i-1,i,0);
int f=spfa(maxx);
if(f)
cout<<d[maxx]<<endl;
}
return 0;
}
The end;