Collectors Problem UVA - 10779

本文介绍了一个基于网络流的最大流问题——贴纸收集问题UVA-10779。通过建立特殊的网络模型,利用Dinic算法求解最大流,以确定Bob能收集到的贴纸种类数量。

Collectors Problem UVA - 10779

网络流·最大流

题目大意:

Bob和他的朋友从糖果包装里收集贴纸。这些朋友每人手里都有一些贴纸(可能重复),并且只跟别人交换他所没有的贴纸。贴纸总是一对一交换。
Bob比这些朋友聪明,因为他意识到只跟别人交换自己没有的贴纸并不总是最优的。
问:Bob最多收集到多少种贴纸?

题解:

把每个人(除Bob)和每种贴纸看成点。
每个贴纸的点向T连1的边,如果有流从这里流过表示可以收集到这种贴纸。
对于除Bob外的每个人,假设他拥有某种贴纸cnt个,
如果cnt>1,从这个人向这种贴纸连cnt-1,表示他最多可以提供cnt-1张。
如果cnt=0,从贴纸向人连边,容量为1,表示他可以接受一个这种他没有的贴纸。
因为人的点流量平衡,也就是
【入流】他得来的(原来他没有的)贴纸的数量
【出流】他被换走的(原来他有超过一张的)贴纸的数量
相等。刚好满足一对一交换。
最后让S代表Bob,向他有的贴纸连cnt的边。这些流量可以直接流向T,也可以用于交换。
最大流即是答案。

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 205;
const int M = 1005;
const int INF = 0x3f3f3f3f;

struct Edge{
    int to,cap,flow,next;
}e[M*2];
int head[N],ec=1;
void clearEdge(){ memset(head,0,sizeof(head)); ec=1; }
void add(int a,int b,int cap){
    ec++; e[ec].to=b; e[ec].cap=cap; e[ec].flow=0;
    e[ec].next=head[a]; head[a]=ec;
}
void add2(int a,int b,int cap){
    add(a,b,cap); add(b,a,0);
}

int S,T,n,m;
int cnt[N][N];
bool vis[N]; int d[N];

void clearAll(){
    memset(cnt,0,sizeof(cnt));
    clearEdge();
}

bool bfs(){
    memset(vis,0,sizeof(vis));
    queue<int> q; q.push(S); vis[S]=true; d[S]=0;
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(e[i].cap>e[i].flow && !vis[v]){
                d[v]=d[u]+1; vis[v]=true; q.push(v);
            }
        }
    }
    return vis[T];
}

int dfs(int u,int a){
    if(u==T || a==0) return a;
    int flow=0, f;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(d[v]==d[u]+1 && (f=dfs(v,min(a,e[i].cap-e[i].flow)))){
            flow+=f; a-=f;
            e[i].flow+=f; e[i^1].flow-=f;
            if(a==0) break;
        }
    }
    if(a>0) d[u]=-1;
    return flow;
}

int dinic(){
    int flow=0;
    while(bfs()){
        flow+=dfs(S,INF);
    }
    return flow;
}

inline int person(int x){ return x; }
inline int object(int x){ return x+n; }
void build(){
    S=n+m+1; T=S+1;
    for(int j=1;j<=m;j++){
        if(cnt[1][j]) add2(S,object(j),cnt[1][j]);
    }
    for(int i=2;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(cnt[i][j]>1) add2(person(i),object(j),cnt[i][j]-1);
            else if(cnt[i][j]==0) add2(object(j),person(i),1);
        }
    }
    for(int j=1;j<=m;j++){
        add2(object(j),T,1);
    }
}

int main(){
    freopen("a.in","r",stdin);
    int T; cin>>T;
    int a,k;
    for(int cas=1;cas<=T;cas++){
        clearAll();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&k);
            while(k--){
                scanf("%d",&a); 
                cnt[i][a]++;
            }
        }
        build();
        int ans=dinic();
        printf("Case #%d: %d\n",cas,ans);
    }
}
collectors.groupingBy是java.util.stream.Collectors类中的一个静态方法,它是用于进行分组操作的。在使用这个方法时,我们需要传递一个Function参数,用于指定按照哪个属性或条件进行分组。 然而,collectors.groupingBy方法也可以与非静态的Lambda表达式一起使用。也就是说,我们可以使用一个非静态的方法来作为groupingBy方法的参数。 例如,假设我们有一个Student类,其中包含了name和age属性。我们想要根据年龄进行学生分组,但是我们希望使用一个非静态的Lambda表达式来定义分组条件。 我们可以这样做: ``` public class Student { private String name; private int age; // constructors, getters, setters // non-static lambda expression public Predicate<Student> ageGreaterThan(int age) { return student -> student.getAge() > age; } } public static void main(String[] args) { List<Student> students = Arrays.asList( new Student("John", 20), new Student("Mike", 22), new Student("Sarah", 18), new Student("Emma", 21) ); Map<Boolean, List<Student>> groups = students.stream() .collect(Collectors.groupingBy(Student::ageGreaterThan(20))); System.out.println("Greater than 20: " + groups.get(true)); System.out.println("Less than or equal to 20: " + groups.get(false)); } ``` 在上面的示例中,我们在Student类中定义了一个非静态的Lambda表达式ageGreaterThan,它返回一个Predicate<Student>对象。我们在groupingBy方法中使用Student::ageGreaterThan(20)来指定分组条件,即将年龄大于20的学生分为一组,将年龄小于或等于20的学生分为另一组。 这样,我们就可以使用非静态的Lambda表达式来进行分组操作,使得代码更加灵活和可读性更高。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值