您的位置:首页 > 其它

Codeforces Round #422 (Div. 2) C. Hacker, pack your bags! 思维排序或二分

2017-07-17 15:08 393 查看
题目:http://codeforces.com/contest/822/problem/C

题意:给多个区间,每个区间都有一个权值,求两个长度和为x的区间的最小权值和。

思路1:像这种找两个区间的问题,对于每个区间只需考虑在他左面的或只需考虑在他右面的即可,不需要都考虑,就像区间1 区间2 和区间2 区间1 效果是一样的

维护一个need【i】表示长度为i的区间的最小权值,至于如何保证区间不交,控制一下维护顺序即可,对原区间按左端点从小到大排序,1->n遍历一遍,设当前区间左端点l,右端点r,权值c,因此长度为r-l+1,这时need【i】表示的通过所有右端点小于l的区间(也就是在此区间左边的区间)维护后的值,控制这个维护的顺序可以用优先队列实现,也可以直接排序,还有一种方法就是以右端点为索引保存这个区间,遍历即可。

优先队列写法:171ms

#include<bits/stdc++.h>
using namespace std;
struct node
{
int le,ri,cost;
bool operator < (const node u)const
{
return ri>u.ri;
}
}num[200005];
int need[200005];
int cmp(node u,node v)
{
if(u.le==v.le)
return u.ri<v.ri;
return u.le<v.le;
}
priority_queue<node>q;
4000

int main()
{
int i,n,x,l,r,c,ban;
cin>>n>>x;
for(i=0;i<n;i++)
scanf("%d %d %d",&num[i].le,&num[i].ri,&num[i].cost);
sort(num,num+n,cmp);
int minn=2e9+2;
for(i=0;i<n;i++)
{
int now=num[i].ri-num[i].le+1;
if(now>=x)
continue;
if(need[x-now])
minn=min(minn,need[x-now]+num[i].cost);
q.push(num[i]);
if(i!=n-1)
while(!q.empty())
{
node t=q.top();

if(t.ri>=num[i+1].le)
break;
q.pop();
// cout<<t.le<<" "<<t.ri<<" "<<t.cost<<endl;
if(need[t.ri-t.le+1])
need[t.ri-t.le+1]=min(need[t.ri-t.le+1],t.cost);
else
need[t.ri-t.le+1]=t.cost;
}
}
if(minn==2e9+2)
cout<<-1<<endl;
else
cout<<minn<<endl;
return 0;
}
排序写法:171ms

#include<bits/stdc++.h>
using namespace std;
struct node
{
int le,ri,cost;
}num[200005],again[200005];
int need[200005];
int cmp(node u,node v)
{
if(u.le==v.le)
return u.ri<v.ri;
return u.le<v.le;
}
int cmp2(node u,node v)
{
return u.ri<v.ri;
}
int main()
{
int i,n,x,l,r,c,ban;
cin>>n>>x;
for(i=0;i<n;i++)
scanf("%d %d %d",&num[i].le,&num[i].ri,&num[i].cost),again[i]=num[i];
sort(num,num+n,cmp);
sort(again,again+n,cmp2);
int minn=2e9+1;
int index=0;
for(i=0;i<n;i++)
{
int now=num[i].ri-num[i].le+1;
if(now>=x)
continue;
if(need[x-now])
minn=min(minn,need[x-now]+num[i].cost);
if(i!=n-1)
while(again[index].ri<num[i+1].le)
{
now=again[index].ri-again[index].le+1;
if(need[now])
need[now]=min(need[now],again[index].cost);
else
need[now]=again[index].cost;
index++;
}
}
if(minn==2e9+1)
cout<<-1<<endl;
else
cout<<minn<<endl;
return 0;
}

右端点vector记录写法:202ms(本以为这个是最优的。。。)
#include<bits/stdc++.h>
using namespace std;
struct node
{
int le,ri,cost;
}num[200005];
int need[200005];
int cmp(node u,node v)
{
if(u.le==v.le)
return u.ri<v.ri;
return u.le<v.le;
}
vector<node>again[200005];
int main()
{
int i,n,x,l,r,c,ban;
cin>>n>>x;
for(i=0;i<n;i++)
scanf("%d %d %d",&num[i].le,&num[i].ri,&num[i].cost),again[num[i].ri].push_back(num[i]);
sort(num,num+n,cmp);
int minn=2e9+1,index=0;
for(i=0;i<n;i++)
{
int now=num[i].ri-num[i].le+1;
if(now>=x)
continue;
if(need[x-now])
minn=min(minn,need[x-now]+num[i].cost);
if(i!=n-1)
for(;index<num[i+1].le;index++)
{
for(int j=0;j<again[index].size();j++)
{
now=again[index][j].ri-again[index][j].le+1;
if(need[now])
need[now]=min(need[now],again[index][j].cost);
else
need[now]=again[index][j].cost;
}
}
}
if(minn==2e9+1)
cout<<-1<<endl;
else
cout<<minn<<endl;
return 0;
}

思路2:二分。和上面思路整体相同,vector【i】记录长度为i的所有区间,遍历每个区间,在长度匹配的区间里二分找合适的,事先求一个权值最小后缀,优化一下即可。

记得上次去西安遇到一个和这个类似的题目,卡了很长时间都没出。现在又是想了半天也没有思路。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: