Bzoj4276 [ONTAK2015]Bajtman i Okrągły Robin

原题网址:http://www.lydsy.com/JudgeOnline/problem.php?id=4276
一开始想在每个时间点贪心,为了正确性,加了一贯贪心的撤销操作,当当前时间点权值最大的能取的因为太晚而取不了就找一个前面能和他交换时间的互换抢劫时间,但WA掉了,然后问了神犇,他提到我这样只能一步交换,有可能连环交换,而且另一个神犇提到了网络流贪心做多半是有退流的问题。 附上WA的贪心代码:

#include<bits/stdc++.h>
const int N = 5050;
struct rec{int a, b, c;};
rec a[N],b[N],heap[N];
int n,cnt,tot,ans;
bool cmp(const rec &a, const rec &b){
    return a.a < b.a;
}
void heapup(int x){
    while (x > 1 && heap[x].c > heap[x >> 1].c)
        std::swap(heap[x >> 1],heap[x]),
        x >>= 1;
}
void heapdown(int x){
    while (x*2 <= cnt && heap[x*2].c > heap[x].c || x*2+1 <= cnt && heap[x*2+1].c > heap[x].c)
        if (x*2+1 <= cnt && heap[x*2+1].c > heap[x*2].c)
            std::swap(heap[x*2+1],heap[x]), x = x*2+1;
        else
            std::swap(heap[x*2],heap[x]), x = x*2;
}
void push(const rec &x){
    heap[++cnt] = x;
    heapup(cnt);
}
void poop(){
    heap[1] = heap[cnt--];
    heapdown(1);
}
int main(){
    scanf("%d",&n);
    for (int i=1; i<=n; i++)
        scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),
        a[i].b--;
    std::sort(a+1,a+n+1,cmp);
    int p = 1;
    for (int tim=1; tim<5000; tim++){
        for (; p <= n && a[p].a == tim; p++)
            push(a[p]);
        bool picked = 0;
        for (; !picked && cnt; poop())
            if (heap[1].b >= tim){
                b[++tot] = heap[1];
                b[tot].a = tim + 1;
                ans += heap[1].c;
                picked = 1;
            }else
                for (int i=1; i<=tot; i++)
                    if (b[i].a <= tim && b[i].b >= tim){
                        b[i].a = tim + 1;
                        ans += heap[1].c;
                        picked = 1;
                        break;
                    }
    }
    printf("%d\n",ans);
    return 0;
}

正解实际是费用流,实质是一个二分图最大权匹配,一边是强盗,另一边是日期,特殊的是因为匹配的是一个区间内的日期,所以可以用线段树优化建图。

#include<bits/stdc++.h>
const int N = 15001;
const int M = 3e5;
const int INF = 1e9;
struct edge{int x,y,f,v,next;} mp[M];
int n,S,T,s,a,b,c,cnt,ans,first[N],q[N + 10],lc[N],rc[N],dis[N],from[N];
bool inq[N];
void ins(int x, int y, int f, int v){
    mp[++s] = (edge){x,y,f,v,first[x]}; first[x] = s;
    mp[++s] = (edge){y,x,0,-v,first[y]}; first[y] = s;
}
void build(int i, int l, int r){
    if (l == r) {ins(i,T,1,0); return;}
    ins(i,lc[i] = ++cnt,INF,0);
    build(cnt,l,(l+r)/2);
    ins(i,rc[i] = ++cnt,INF,0);
    build(cnt,(l+r)/2+1,r);
}
void find(int k, int i, int l, int r, int _l, int _r){
    if (l > _r || r < _l) return;
    if (l >= _l && r <= _r) {ins(k,i,1,0); return;}
    find(k,lc[i],l,(l+r)/2,_l,_r);
    find(k,rc[i],(l+r)/2+1,r,_l,_r);
}
bool SPFA(){
    int head = 1, tail = 2;
    memset(inq,0,sizeof(inq));
    for (int i=S; i<=T; i++) dis[i] = -INF;
    dis[q[head] = S] = 0;
    inq[q[head]] = 1;
    while (head != tail){
        int x = q[head++];
        if (head > N) head = 1;
        for (int t=first[x]; t>0; t=mp[t].next){
            int y = mp[t].y;
            if (mp[t].f && dis[x] + mp[t].v > dis[y]){
                dis[y] = dis[x] + mp[t].v;
                from[y] = t;
                if (!inq[y]){
                    inq[q[tail++] = y] = 1;
                    if (tail > N) tail = 1;
                }
            }
        }
        inq[x] = 0;
    }
    return dis[T] > -INF;
}
void mcf(){
    for (int x=T; x!=S; x=mp[from[x]].x)
        mp[from[x]].f--,
        mp[from[x]^1].f++;
    ans += dis[T];
}
int main(){
    scanf("%d",&n);
    S = 0; T = 15000; s = 1;
    build(cnt = n + 1,1,5000);
    for (int i=1; i<=n; i++)
        scanf("%d%d%d",&a,&b,&c),
        ins(S,i,1,c),
        find(i,n+1,1,5000,a,b-1);
    while (SPFA()) mcf();
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值