题目大意:给出N个区间[Li,Ri](1<=i<=N),一个正整数M,求N个区间里,并区间包含[0,M]的区间的最小个数(无解时输出:No solution)。
Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u
数据规模:1<=M<=5000,-50000<=Li<Ri<=50000,1<=N<=99999。
理论基础:无。
题目分析:用贪心法。
首先,预处理,除去所有可以被别的区间包含的区间,可以证明,这样以后,得出的最优解不会劣于未处理前。
证明:假设原解用到区间[a,b]包含于[m,n]那么将原解中的区间替换为[m,n]答案不会变劣,证毕。
还可以证明,在所有能够覆盖左端点的区间中选择最靠近左端点的区间得到的解不会劣于原解。
证明:假设原解中有区间[a,b],现有区间[m,n],a<m由预处理知,各区间互不包含所以b<n,那么[a,b]覆盖的区域,[m,n]也可以将其覆盖,因为[m,n]也可以覆盖左端点,而n>b,所以必然也可以覆盖右端点,这样结果不劣于原解。所以命题得证。
贪心策略:每次找到一个区间后,将左端点更新为此区间的右端点,再进行寻找。直到找到的区间的右端点覆盖了M为止。
最后,我们来证明这样贪心得出的解必为最优解,假设不是最优解,那么必然有一个区间可以去除。
1.假设此区间为第一个区间,则根据贪心策略,有第二个区间不覆盖左端点(如果覆盖的话,选择的第一个区间即为此区间),去掉第一个区间后,则左端点没有被覆盖,所以此区间不可去除。
2.假设此区间为最后一个区间,同1,如果去除此区间后,则右端点未被覆盖,所以不可去除。
3.假设此区间为相邻的三个区间[a,b],[x,y],[m,n]中的[x,y]。则根据贪心策略存在d属于区间[x,y]有:a<x<b<m<y<n,如果去掉[x,y],则区间[b,m]未被覆盖,所以不可去除。
综上所述,贪心策略所得解中所有区间都不可去除,所以必为最优解。得证。
代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<ctime>
#include<vector>
using namespace std;
typedef double db;
#define DBG 0
#define maa (1<<31)
#define mii ((1<<31)-1)
#define ast(b) if(DBG && !(b)) { printf("%d!!|\n", __LINE__); while(1) getchar(); } //调试
#define dout DBG && cout << __LINE__ << ">>| "
#define pr(x) #x"=" << (x) << " | "
#define mk(x) DBG && cout << __LINE__ << "**| "#x << endl
#define pra(arr, a, b) if(DBG) {\
dout<<#arr"[] |" <<endl; \
for(int i=a,i_b=b;i<=i_b;i++) cout<<"["<<i<<"]="<<arr[i]<<" |"<<((i-(a)+1)%8?" ":"\n"); \
if((b-a+1)%8) puts("");\
}
template<class T> inline bool updateMin(T& a, T b) { return a>b? a=b, true: false; }
template<class T> inline bool updateMax(T& a, T b) { return a<b? a=b, true: false; }
typedef long long LL;
typedef long unsigned int LU;
typedef long long unsigned int LLU;
#define N 100000
pair <int,int> s[N+5],pre[N+5],ans[N+5];
int m,cnt,np,na;
ostream& operator << (ostream& out,pair <int,int> a)
{
printf("%d %d\n",a.first,a.second);
return out;
}
bool fun(pair <int,int> a,pair <int,int> b)
{
if(a.first!=b.first)return a.first<b.first;
else return a.second<b.second;
}
void solve()
{
int left=0,right=m,now=0;
while(now<=np&&left<right)
{
while(now<=np&&pre[now].first<=left)now++;
if(now==0)break;
if(pre[now-1].first<=left)
{
ans[na++]=pre[now-1];
left=pre[now-1].second;
}
}
if(left>=right)
{
printf("%d\n",na);
for(int i=0;i<na;i++)cout<<ans[i];
}
else printf("No solution\n");
}
int main()
{
scanf("%d",&m);
while(1)
{
int a,b;
scanf("%d%d",&a,&b);
if(a!=0||b!=0)
{
s[cnt].first=a;
s[cnt++].second=b;
}
else break;
}
sort(s,s+cnt,fun);
pra(s,0,cnt-1)
pre[0]=s[0];
for(int i=1;i<cnt;i++)
{
if(s[i].first==pre[np].first)pre[np]=s[i];
else if(s[i].second<=pre[np].second);
else pre[++np]=s[i];
}
pra(pre,0,np)
solve();
return 0;
}
其中,ans[i]即为用来存储所选区间的数组。
by:Jsun_moon http://blog.csdn.net/Jsun_moon