太空飞行计划【题解】网络流

6 篇文章 0 订阅
4 篇文章 0 订阅

前言

蒟蒻初涉及图论,不会做

sol

这道题,一个显然的建图是用S向每一个实验连一条流量为赞助费的边,相当于是你做了这个实验给你的报酬。
又一个显然的连边是把实验连向仪器,作为该实验的一个开支,该实验对成本的获得。这一个流量显然是INF,因为你肯定是要把你得到的报酬都尽可能去先把实验仪器买齐,所以肯定没有用经费限制。
仪器显然有的出边是仪器的成本,当这条边的残量为0时,证明这个仪器买到了,否则就说明这个仪器没有买到。这条出边连向何方,暂且不知道,但知道流出去的那些钱相当于已经用掉了,不能有什么别的用了,所以十有八九流向汇点。
剩下还要连什么边?上述都显然是必连边。
比如说可以把实验与试验连起来,这就相当于某个实验的经费不够了,别的有剩余的来帮他。这就是不对的,因为我们可以不选一些实验。换句话说,这样等价于把所有的实验看成一个实验,这样就没有意义了。
还可以把仪器都连起来,但这跟上面那个一样,搞了以后就没有决策的必要了。
似乎不能连什么边了。。
看一下当前的图的意义,发现,就是相当于假设所有实验都选,然后看一看在这样下,每一个实验会剩多少钱,剩的钱就在实验与仪器的连边里待着。
对,就是这么简单的意义。
看上去太弱智了,好像谁都会搞一样。
然后就是,某一个实验的收益如果是大于等于0的,应该要选。
这样对吗?要知道,他可能是因为别的实验帮他出了钱买了他俩共有的仪器。
那就把那个帮他出了钱的也要了。
就是这么干脆,因为可以保证,那个帮他出了钱的实验自己肯定剩了钱(剩了0元也是钱),因为他要是自己缺钱,他就不会帮别人了。
那么如果一个实验收益是正的,可以不选吗?
这样做太不优秀,他剩了钱,说明他还帮别人出了点钱。
所以,解法显然,那样连边就可以了.
最后,统计答案时,如果它剩了0元,他可能不选,也可能选,很难通过这一个来抉择。
实际上,统计答案十分麻烦。我的做法是,如果剩了钱,就不管,肯定选。如果没有省钱,就给它一块钱,然后让他跑一遍网络流,如果成功更新了最大流,就不选,如果没有更新就选。

code

#include<bits/stdc++.h>
using namespace std;
bool innik=0;
inline char gc(){
    static char buf[1<<6],*p1=buf,*p2=buf;
    return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
inline void read(T&data){
    data=0;innik=0;
    register char ch=0;
    while(ch<'0'||ch>'9')ch=gc();
    while(ch<='9'&&ch>='0'){
        data=(data<<3)+(data<<1)+(ch&15);
        ch=gc();
    }
    if(ch=='\r'||ch=='\n')innik=1;
    return ;
}
const int INF  =2e9,_ =105,__ =10010;
int m,n;
int to[__<<1],nxt[__<<1],head[_],cur[_],w[__<<1],cnt=-1,deep[_],S,T;
inline void add(register int a,register int b,register int c){
    to[++cnt]=b,nxt[cnt]=head[a],head[a]=cnt,w[cnt]=c;
    to[++cnt]=a,nxt[cnt]=head[b],head[b]=cnt,w[cnt]=0;
}
inline bool bfs(){
    queue<int >Q;
    while(!Q.empty())Q.pop();
    memset(deep,0,sizeof(deep));
    deep[S]=1;
    Q.push(S);
    while(!Q.empty()){
        register int now=Q.front();Q.pop();
        for(register int i=head[now];~i;i=nxt[i]){
            if(deep[to[i]])continue;
            if(w[i]<=0)continue;
            deep[to[i]]=deep[now]+1;
            Q.push(to[i]);
        }
    }
    return deep[T]!=0;
}
int dfs(register int now,register int flow){
    if(now==T)return flow;
    for(register int &i=cur[now];~i;i=nxt[i]){
        if(w[i]<=0||deep[to[i]]!=deep[now]+1)continue;
        register int di=dfs(to[i],min(flow,w[i]));
        if(di>0){
          //  cout<<di<<' '<<to[i]<<' ';
            w[i]-=di,w[i^1]+=di;//cout<<w[i]<<' '<<i<<endl;
            return di;
        }
    }
    return 0;
}
inline int dinic(){
    register int ret=0;
    while(bfs()){
        for(register int i=1;i<=T;++i)cur[i]=head[i];
        while(int d=dfs(S,INF))ret+=d;          
    }
    return ret;
}
int sum[_],tong1[_],tong2[_],val[_],tong3[_],vis[_];
bool Dfs(register int now,register int flow){
    if(now==T)return flow;
    //cout<<now<<endl;
    vis[now]=1;
    for(register int i=head[now];~i;i=nxt[i]){
        if(w[i]==0)continue;
        if(vis[to[i]])continue;
        register int di=Dfs(to[i],min(flow,w[i]));
        if(di>0){
            return 1;
        }
    }
    return 0;
}
int main(){
    freopen("data.in","r",stdin);
    freopen("1.out","w",stdout);
    memset(head,-1,sizeof(head));
    read(m),read(n);
    S=m+n+1,T=S+1;innik=0;
    for(register int i=1,cost,a;i<=m;++i){
        read(cost);
        add(S,i,cost);val[i]=cost;
        while(!innik){
            read(a);sum[i]++;
            add(i,a+m,INF);
        }
        innik=0;
    }
    for(register int i=1;i<=n;++i){
        register int a;read(a);val[i+m]=a;
        add(i+m,T,a);
    }
    register int ans=dinic();ans=0;
    for(register int i=head[S];~i;i=nxt[i]){
        if(w[i]==0){
            memset(vis,0,sizeof(vis));
            if(!Dfs(to[i],1)){
                tong1[to[i]]=1;
            }
        }
        else {
            tong1[to[i]]=1;
        }
    }
    for(register int i=1;i<=m;++i){
        if(tong1[i]){
            cout<<i<<' ',ans+=val[i];
            for(register int j=head[i];~j;j=nxt[j]){
                if(to[j]!=S)tong2[to[j]]=1;
            }
        }
    }cout<<endl;
    for(register int i=m+1;i<=m+n;++i){
        if(tong2[i]){
            cout<<i-m<<' ' ;ans-=val[i];
        }
    }
    cout<<endl<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值