【序列长度】解题报告

来源:互联网 发布:中国家庭金融调查数据 编辑:程序博客网 时间:2024/05/19 12:12

4.序列长度

【问题描述】

  有一个整数序列,我们不知道她的长度是多少(即序列中整数的个数),但我们知道在某些区间中至少有多少个整数,用区间 [ai,bi,ci]来描述它,[ai,bi,ci]表示在该序列中处于[ai,bi]这个区间的整数至少有ci个。现在给出若干个这样的区间,请你求出满足条件的最短序列长度是多少。如果不存在则输出 -1。

【文件输入】

   第一行包括一个整数n(n<=1000),表示区间个数;

   以下n行每行描述这些区间,第i+1行三个整数ai,bi,ci,由空格隔开,其中0<=ai<=bi<=1000而且1<=ci<=bi-ai+1。

【文件输出】

    文件输出只有一个整数表示满足要求序列长度的最小值。

【样例输入】

 5

 3 7 3

 8 10 3

 6 8 1

 1 3 1

 10 11 1【样例输出】

 6

提示:

其中两个合法序列分别为(3,5,7,8,9,10)(3,4,6,8,9,10),长度都为6,不可能有比6更短的合法序列了.

注意:序列中不能有相同数字.

 

这道题有点难度,只有biamgo和汪维正用了最优解法。查分约束系统。(这道题还有贪心方法、动规方法(不正确))

//===============================================================

 

把问题抽象成一个数组T。

题中给出很多的限制条件:


用前缀和简化为


 

再类比spfa中,u为松弛v的点


因此我们可以把没个S抽象为一个点,

从u到v建立一条权值为-map[u,v]的边。

 

这样做是错误的。还有一个隐藏的限制条件


这个是容易想到的,

因此还需要加两条边。

 

代码实现上,有两种方案,一是用一个超级源,二是首先把所有的元素入队,我采用了第二种方法,因为此题中0号节点是有实际意义的,因此只好用m+1来表示空。

另外一点,就是差分约束不能够像spfa那样手动对初始点进行松弛,因此应该把每个点的d值设为相同,最后输出两个d的差,只表示相对高度。

 

一开始一直错在加边应该是(a[i],b[i]-1),我写成(a[i],b[i])了。

//===============================================================

spfa中如果存在一个负权回路,就会对一个点无限次地松弛。

因为一个点最多被松弛n次(易证),如果超过n次说明存在负权回路,即无解

//================================================================

 

//#include <iostream>//using std::cout;//using std::cin;#include <cstdio>#include <bitset>const long oo = 0x7fff0000;std::bitset<1002> q;long a[1002];long b[1002];long c[1002];long n;long s[1002];bool used[1002];long l=0;long r=0;long que[1000002];long m = 0;long count[1002];struct node{long index;long data;node* next;};node* bian[100002];void spfa(){while (l<r){long now = que[++l];used[now] = false;node* ths = bian[now];while (ths){long i = ths->index;if (s[i] > s[now]+ths->data){count[i]++;if (count[i]>n){printf("-1");exit(0);}s[i] = s[now]+ths->data;if (!used[i]){used[i] = true;que[++r] = i;}}ths = ths->next;}}}void insert(long a,long b,long c){node* tmp = new node;tmp->index = b;tmp->data = c;tmp->next = bian[a];bian[a] = tmp;}int main(){freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);scanf("%ld",&n);for (long i=1;i<n+1;i++){scanf("%ld%ld%ld",a+i,b+i,c+i);//map[b[i]][a[i]-1] = -c[i];insert(b[i],a[i]-1,-c[i]);m >?= b[i];m >?= a[i];}for (long i=1;i<m+1;i++){//map[i][i-1] = 0;//map[i-1][i] = 1;insert(i,i-1,0);insert(i-1,i,1);}insert(0,m+1,0);insert(m+1,0,1);for (long i=0;i<m+1;i++){que[++r] = i;}spfa();printf("%ld",s[m]-s[m+1]);return 0;}

贪心:


按照右边界的升序排列,

从A点向左必然满足数列上数量为c[i],

然后从B点向左,必然首先满足上一个条件,然后如果不够再继续在空隙里面填,

依此类推。填的时候尽量从右节点开始向左填,尽量让多个区间共享?


#include <iostream>using std::cout;//using std::cin;#include <cstdio>#include <bitset>const long oo = 0x7fff0000;struct node{long a;long b;long c;};node qj[1002];std::bitset<1002> q;long n;int bigger(const void* a,const void* b){node* aa = (node*)a;node* bb = (node*)b;if (aa->b>bb->b)return 1;if (aa->b<bb->b)return -1;return 0;}int main(){freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);scanf("%ld",&n);for (long i=1;i<n+1;i++){scanf("%ld%ld%ld",&qj[i].a,&qj[i].b,&qj[i].c);}qsort(qj+1,n,sizeof(node),&bigger);if (n==0){printf("-1");return 0;}if (n==1){printf("%ld",qj[1].c);return 0;}//if (n==5){printf("6");return 0;}for (long i=1;i<n+1;i++){long ss = qj[i].c;for (long j=qj[i].a;j<qj[i].b+1;j++){if (q.test(j)){ss--;}}long tt = qj[i].b;while (ss>0){while (tt>=0&&q.test(tt)){--tt;}if (tt<0){printf("-1");return 0;}q.set(tt);ss--;}}printf("%ld",q.count());return 0;}


原创粉丝点击