2726 Plan(最大费用流)

类似二取方格数 , fj有2个房子出租,1000个order , 给出order的时间范围和pay,求如何安排能得到最大的收益

TLE的构图方式:以order为点,设一组源汇和一组超级源汇,对每个order拆点,权值为1 , 花费为-c ,没有时间冲突的order之间连边权值为1 , 花费为0 。

这种方法果断超时了,2000个点4000000条边。【50组数据跑6s多 , 标程0.125s】

膜拜了torry的代码之后的改进构图方法:以时间为点,相邻时间到时间都连边w=2 , c=0 ,order的边直接加在点间 , 但这里要处理下就是order的结束时间要+1 , 这样可以去点反身边 以及避免2个order有相同时间的冲突会同时选上的错误。

spfa的费用流还是很给力的

【torry 牛出题 ,当时标程数组开小了以为是数据错误。。。】

#include <cstdio>
#include <cstring>
#define min(a,b) (a>b?b:a)
#define max(a,b) (a<b?a:b)

using namespace std;

const int maxn=410;
const int inf=2000000000;
int n;
struct Edge{
    int v,next,c,w;
}edge[3500];
int head[maxn],cnt;

void addedge(int u,int v,int w,int c)
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].c=c;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].v=u;
    edge[cnt].w=0;
    edge[cnt].c=-c;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}

//for spfa and mcmf
int dis[maxn],pre[maxn];//最小费用和前驱结点
int alpha[maxn];//标记
int que[maxn],qhead,qrear;
int spfa (int s,int e)//源汇点
{
    //寻找费用增广,没有返回-1;
    for (int i=0 ; i<maxn ; ++i)
        dis[i]=inf;
    memset (alpha , 0 , sizeof(alpha));
    dis[s]=0;
    que[qhead=0]=s;
    qrear=1;
    alpha[s]=1;
    while (qhead!=qrear)//头可能大于尾
    {
        int k=que[qhead++];
        qhead%=maxn;//循环队列
        alpha[k]=0;
        for (int q=head[k] ; ~q ; q=edge[q].next)
            if(edge[q].w)
                if(dis[k]+edge[q].c<dis[edge[q].v])
                {
                    dis[edge[q].v]=dis[k]+edge[q].c;
                    pre[edge[q].v]=q;
                    if(!alpha[edge[q].v])
                    {
                        alpha[edge[q].v]=true;
                        if(edge[q].c<0)
                        {
                            qhead=(qhead-1+maxn)%maxn;
                            que[qhead]=edge[q].v;
                        }
                        else
                        {
                            que[qrear++]=edge[q].v;
                            qrear%=maxn;
                        }
                    }
                }
    }
    if(dis[e]==inf)return -1;
    //终点不可达,返回-1;
    int k=inf;
    for(int i=e ; i!=s ; i=edge[pre[i]^1].v)
        k=min(k,edge[pre[i]].w);
    //sum+=k;//sum记录最大流(有些题里会用到)
    return k;//返回该可行流流量
}

struct order {
    int a,b,c;
}o[maxn];

int mcmf(int s,int t)
{
    int ans=0,k;
    while (~(k=spfa(s,t)))
    {
        //printf("!!!\n");
        for (int i=t ; i!=s ; i=edge[pre[i]^1].v)
        {
            edge[pre[i]].w-=k;
            edge[pre[i]^1].w+=k;
        }//更新流
        ans+=dis[t]*k;//最小费用*流量
    }
    return ans;
}
///以order为点的构图会超时,拆点后2000的点,2000*2000的边
/*
void build_graph()
{
    cnt=0;
    memset (head , -1 , sizeof(head));
    addedge(0 , 1 , 2 , 0);
    addedge(2*n+2 , 2*n+3 , 2 , 0);
    for (int i=0 ; i<n ; ++i)
    {
        addedge(i+2 , i+2+n , 1 , -o[i].c);
        addedge(1 , i+2 , 2 , 0);
        addedge(i+2+n , 2*n+2 , 1 , 0);
        for (int j=0 ; j<n ; ++j)
        {
            if(o[i].b<o[j].a)addedge(i+2+n , j+2 , 1 , 0);
        }
    }
}
*/
///以时间为点构图 , 400个点 , 1000条边 S=0  t=402
void build_graph()
{
    cnt=0 ;
    memset (head , -1 , sizeof(head));

    for (int i=0 ; i<=401 ; ++i)
    {
        addedge(i , i+1 , 2 , 0);
    }
    for (int i=0 ; i<n ; ++i)
    {
        addedge(o[i].a+1 , o[i].b+2 , 1 , -o[i].c);
    }
}

int main()
{
    //freopen ("data.txt","r",stdin);
    //freopen ("std.txt","w",stdout);
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        scanf("%d",&n);
        for (int i=0 ; i<n ; ++i)
        {
            scanf("%d%d%d",&o[i].a , &o[i].b , &o[i].c);
        }
        build_graph();
        int ans=mcmf(0 , 402);
        printf("%d\n",-ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值