bzoj 1930: [Shoi2003]pacman 吃豆豆 [费用流]

1930: [Shoi2003]pacman 吃豆豆

题意:两个PACMAN吃豆豆。一开始的时候,PACMAN都在坐标原点的左下方,豆豆都在右上方。PACMAN走到豆豆处就会吃掉它。PACMAN行走的路线很奇怪,只能向右走或者向上走,他们行走的路线不可以相交。 请你帮这两个PACMAN计算一下,他们俩加起来最多能吃掉多少豆豆。


暴力建图会被卡内存,可以按lis剖分建图...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
#define fir first
#define sec second
const int N = 5005, M = 1e6+5, mo = 1e9+7;
inline int read() {
    char c=getchar(); int x=0,f=1;
    while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n, s, t, ss, tt;
struct meow {
    int x, y;
    bool operator <(const meow &r) const {return x == r.x ? y < r.y : x < r.x;}
} a[2005];

struct edge {int v, ne, c, f, w;} e[M];
int cnt = 1, h[N];
inline void ins(int u, int v, int c, int w) { //printf("ins %d --> %d\n", u, v);
    e[++cnt] = (edge) {v, h[u], c, 0, w};  h[u] = cnt;
    e[++cnt] = (edge) {u, h[v], 0, 0, -w}; h[v] = cnt;
}

void build() {
    s=0; ss=n+n+1; tt=n+n+2; t=n+n+3;
    ins(s, ss, 2, 0); ins(tt, t, 2, 0);
    static bool mark[N];
    for(int i=1; i<=n; i++) {
        ins(i, n+i, 1, 1); ins(i, n+i, 1, 0);
        int high = 0;
        for(int j=i-1; j>=1; j--) 
            if(a[j].y > high && a[j].y <= a[i].y) ins(j+n, i, 2, 0), high = a[j].y, mark[j] = 1;
        if(!high) ins(ss, i, 2, 0);
    }
    for(int i=1; i<=n; i++) if(!mark[i]) ins(i+n, tt, 2, 0);
}
namespace flow {
    int d[N], inq[N], q[N], head, tail; 
    pair<int, int> pre[N];
    inline void lop(int &x) {if(x == N) x = 1; else if(x == 0) x = N-1;}
    bool spfa() {
        head = tail = 1;
        //memset(d, -1, sizeof(d));
        for(int i=s; i<=t; i++) d[i] = -1e9;
        memset(inq, 0, sizeof(inq));
        d[s] = 0; q[tail++] = s; inq[s] = 1;
        pre[t].fir = -1;
        while(head != tail) {
            int u = q[head++]; inq[u] = 0; lop(head); 
            for(int i=h[u]; i; i=e[i].ne) if(e[i].c > e[i].f) {
                int v = e[i].v;
                if(d[v] < d[u] + e[i].w) {
                    d[v] = d[u] + e[i].w;
                    pre[v] = make_pair(u, i);
                    if(!inq[v]) {
                        inq[v] = 1;
                        //if(d[v] > d[q[head]]) head--, lop(head), q[head] = v;
                        //else q[tail++] = v, lop(tail);
                        q[tail++] = v; lop(tail);
                    }
                }
            }
        }
        return pre[t].fir != -1;
    }
    int mcmf() {
        int flow = 0, cost = 0;
        while(spfa()) {
            int f = 1e9, x;
            for(int i=t; i != s; i = pre[i].fir) x = pre[i].sec, f = min(f, e[x].c - e[x].f);
            flow += f; cost += d[t] * f; 
            for(int i=t; i != s; i = pre[i].fir) x = pre[i].sec, e[x].f += f, e[x^1].f -= f;
        }
        return cost;
    }
}
void print(meow &a) {printf("(%d, %d)\n", a.x, a.y);}
int main() {
    freopen("in", "r", stdin);
    n = read();
    for(int i=1; i<=n; i++) a[i].x = read(), a[i].y = read();
    sort(a+1, a+n+1);
    //for(int i=1; i<=n; i++) print(a[i]);
    build();
    int ans = flow::mcmf();
    printf("%d\n", ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值