hdu 6532 费用流

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6532

 

题意:

      在1e9*1e9的棋盘上给了你n个棋子(n<=500),同时给了你m条限制,每条限制是一个 s a b的结构,s为R或者C的字符,R表示是在第a和a-1行之间画一条线,在这条线的下面最多只能取b个点,C同理,是在线的右边只能取这么多点,每个点i带有的权值是i。问你所有要求都满足的情况下,最大可以取到的权值是多少。

 

做法:

      差点没看出来是一道费用流,其实这个大的棋盘只是一个幌子,其实限制就只有行最多500条,列最多500条,只要好好离散化就好了。为什么说是费用流呢,我们把每个需要的行和列都当做一个点,行从序号小的向大的连边(因为保证是递减的,所以这个流量正常加,在下面被用掉了的话上面就不需要了),列从大的向小的连,其实每个点只需要和上面最近的一个行和左边最近的一个列两个点两边就好了,一个点拆成两个点(网络流常规操作),经过的流量是1,费用就是权值的负值(因为要求最大),然后列和行的最小和源点汇点连起来就好了。

     具体看代码吧。


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5000;
const int maxm=100005;
const int inf=0x3f3f3f3f;
int dis[maxn];
int vis[maxn],pre[maxn];
int head[maxn],cnt;
int n,m,sp,tp;
ll ans=0;
struct node{
    int to,cap,cost,next;
}e[maxm];
void add(int from,int to,int cap,int cost){
    //printf("from=%d to=%d cp=%d cost=%d\n",from,to,cap,cost);
    e[cnt].to=to; e[cnt].cap=cap;
    e[cnt].cost=cost; e[cnt].next=head[from];
    head[from]=cnt++;

    e[cnt].to=from; e[cnt].cap=0;
    e[cnt].cost=-cost; e[cnt].next=head[to];
    head[to]=cnt++;
}
bool spfa(int s,int t,int &flow,int &cost){
    queue<int> q;
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    dis[s]=0;  q.push(s);
    vis[s]=1;
    int d=inf;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;
            if(e[i].cap>0&&dis[v]>dis[u]+e[i].cost){
                dis[v]=dis[u]+e[i].cost;
                pre[v]=i;
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(dis[t]==inf){
        return false;
    }
    for(int i=pre[t];~i;i=pre[e[i^1].to]){
        d=min(d,e[i].cap);
    }
    for(int i=pre[t];~i;i=pre[e[i^1].to]){
        e[i].cap-=d;
        e[i^1].cap+=d;
        cost+=e[i].cost*d;
    }
    flow+=d;
    return true;
}
int mcmf(int s,int t){
    int flow=0,cost=0;
    while(spfa(s,t,flow,cost)){
        //cout<<flow<<" "<<cost<<endl;
    }
    return cost;
}
const int maxp=100005;
struct nn{
    int ats,hav;
    nn(){}
    nn(int ats,int hav):ats(ats),hav(hav){}
    bool operator < (const nn &a)const{
        if(ats==a.ats) return hav<a.hav;
        return ats<a.ats;
    }
} prer[maxp],prec[maxp];
//原先的限制情况,排序要按小的序号和小的k先来
//这样可以保证在后面预处理的时候同样高度的只取小的
//并且从序号小->大的时候k是递减的
struct nnn{
    int id,ats;
    nnn(){}
    nnn(int id,int ats):id(id),ats(ats){}
    bool operator < (const nnn &a)const{
        return ats<a.ats;
    }
    bool operator == (const nnn &a)const{
        return id==a.id&&ats==a.ats;
    }
} neex[maxn],neey[maxn];
//这里是给n个点用的会用map离散化的点
//重定义==是方便去重
int numr,numc,ctr,ctc;
int x[maxn],y[maxn];
map<int,int> mpr,mpc;
int main(){
    memset(head,-1,sizeof(head));
    ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x[i],&y[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        char ss[5];
        int xx,yy;
        scanf("%s%d%d",ss,&xx,&yy);
        if(ss[0]=='R'){
            ctr++;
            prer[ctr].ats=xx,prer[ctr].hav=yy;
        }
        else{
            ctc++;
            prec[ctc].ats=xx,prec[ctc].hav=yy;
        }
    }
    ctr++;
    prer[ctr].ats=1,prer[ctr].hav=inf;
    ctc++;
    prec[ctc].ats=1,prec[ctc].hav=inf;
    sort(prer+1,prer+1+ctr);
    sort(prec+1,prec+1+ctc);
    int ha=prer[1].hav,ct=1;
    for(int i=2;i<=ctr;i++){
        if(prer[i].ats==prer[i-1].ats) continue;
        ct++;
        prer[ct]=prer[i];
        ha=min(ha,prer[i].hav);
        prer[ct].hav=ha;
    }
    ctr=ct;
    ha=prec[1].hav; ct=1;
    for(int i=2;i<=ctc;i++){
        if(prec[i].ats==prec[i-1].ats) continue;
        ct++;
        prec[ct]=prec[i];
        ha=min(ha,prec[i].hav);
        prec[ct].hav=ha;
    }
    ctc=ct;
    for(int i=1;i<=n;i++){
        nn nowx=nn(x[i],inf);
        int needx=upper_bound(prer+1,prer+1+ctr,nowx)-prer-1;
        neex[++numr].id=needx;
        neex[numr].ats=prer[needx].ats;
        nn nowy=nn(y[i],inf);
        int needy=upper_bound(prec+1,prec+1+ctc,nowy)-prec-1;
        neey[++numc].id=needy;
        neey[numc].ats=prec[needy].ats;
    }
    sort(neex+1,neex+1+numr);
    sort(neey+1,neey+1+numc);
    numr=unique(neex+1,neex+1+numr)-neex-1;
    numc=unique(neey+1,neey+1+numc)-neey-1;
    //1-500是r 501-1000是点1 1001-1500是点2 1501-2000是c
    ct=1;
    for(int i=1;i<=numr;i++){
        mpr[neex[i].ats]=ct;
        ct++;
    }
    ct=1;
    for(int i=1;i<=numc;i++){
        mpc[neey[i].ats]=ct;
        ct++;
    }
    sp=0;tp=2001;
    add(sp,1,prer[neex[1].id].hav,0);
    for(int i=1;i<numr;i++){
        add(i,i+1,prer[neex[i+1].id].hav,0);
    }
    for(int i=1;i<numc;i++){
        add(1500+i+1,1500+i,prec[neey[i+1].id].hav,0);
    }


    add(1501,tp,prec[neey[1].id].hav,0);
    for(int i=1;i<=n;i++){
        nnn aa=nnn(0,x[i]);
        int needx=upper_bound(neex+1,neex+1+numr,aa)-neex-1;
        add(mpr[neex[needx].ats],500+i,1,0);
        aa=nnn(0,y[i]);
        int needy=upper_bound(neey+1,neey+1+numc,aa)-neey-1;
        add(1000+i,1500+mpc[neey[needy].ats],1,0);
        add(500+i,1000+i,1,-i);
    }

    cout<<-mcmf(sp,tp)<<endl;

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值