ZOJ 3717: Final Exam Arrangement

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3721


题意:

给出若干课程的上课区间,

若两门课程的上课时间不重叠,则可能有学生同时选修这两门课程,因此不能安排在一天考试。

问至少要安排多少天考试,并输出方案


算法:

线段树优化的DP

首先把课程按照结束时间排序。

对于一个课程,

处理出所有结束时间在它的开始时间之前的课程(也就是与它不相交的课程),

求出它们的考试日期最晚在哪一天,然后把这个日期+1,就得到了这个当前处理的这个课程的最早考试时间。

所有的课程都按照最早考试时间考试,总的考试时间就会最短。

其实这个求解过程的证明不是特别严谨,期待有更严谨的证明。


代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef pair<int,int> PII;

const int MAXN=300000;
int s[MAXN],t[MAXN];
int tr[MAXN<<2];
vector<int> hash;
vector<int> ans[MAXN];
vector<pair<PII,int> > a;

int query(int l, int r, int L, int R, int rt) {
    if(L<=l&&r<=R) {
        return tr[rt];
    }
    int mid=(l+r)>>1;
    int tmp=0;
    if(L<=mid) {
        tmp=max(tmp,query(l,mid,L,R,rt<<1));
    }
    if(R>mid) {
        tmp=max(tmp,query(mid+1,r,L,R,rt<<1|1));
    }
    return tmp;
}

void update(int l, int r, int x, int c, int rt) {
        tr[rt]=max(tr[rt],c);
        if(l==r) {
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) {
        update(l,mid,x,c,rt<<1);
    } else {
        update(mid+1,r,x,c,rt<<1|1);
    }
}

int main() {
    int n;
    while(scanf("%d",&n)==1) {
        hash.clear();
        a.clear();
        for(int i=1; i<=n; i++) {
            ans[i].clear();
        }
        for(int i=1; i<=n; i++) {
            scanf("%d%d",&s[i],&t[i]);
            hash.push_back(s[i]);
            hash.push_back(t[i]);
            a.push_back(make_pair(make_pair(t[i],s[i]),i));
        }
        sort(hash.begin(),hash.end());
        hash.erase(unique(hash.begin(),hash.end()),hash.end());
        sort(a.begin(),a.end());
        int m=hash.size();
        memset(tr,0,sizeof(tr));
        for(int i=0; i<n; i++) {
            a[i].first.second=lower_bound(hash.begin(),hash.end(),a[i].first.second)-hash.begin();
            a[i].first.first=lower_bound(hash.begin(),hash.end(),a[i].first.first)-hash.begin();
        }
        int num=1;
        for(int i=0; i<n; i++) {
            int tmp=query(0,m-1,0,a[i].first.second,1)+1;
            num=max(num,tmp);
            ans[tmp].push_back(a[i].second);
            update(0,m-1,a[i].first.first,tmp,1);
        }
        printf("%d\n",num);
        for(int i=1; i<=num; i++) {
            sort(ans[i].begin(),ans[i].end());
            for(int j=0; j<ans[i].size(); j++) {
                if(j) {
                    printf(" ");
                }
                printf("%d",ans[i][j]);
            }
            puts("");
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值