前言
蒟蒻初涉及图论,不会做
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;
}