文理分班

en,被JD大佬强行思考了一下建图,趁着记得,写一篇题解

注:前面二分图解法,后面网络流解法

题目

image

二分图解法

提前处理好位置与人的关系,跑匈牙利算法,注释在代码上

题解
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1050;
int n,k,m,vis[maxn],match[maxn],ans,g[maxn][maxn],a[maxn],b[maxn];
int find(int u){
    for(int i=1;i<=n;i++){
        if(g[u][i]&&!vis[i]){//u这个位置能坐i这个人且i这个人没坐其他位置 
            vis[i]=1;
            if(!match[i] || find(match[i])){//如果这没人,或这个位置有其他人可以 
                match[i] = u;//i可以坐这个位置 
                return 1;
            }
        }
    }
    return 0;
}
int main(){
    scanf("%d",&m);
    while(m--){
        memset(g,0,sizeof(g));
        memset(match,0,sizeof(match));//初始化 
        scanf("%d", &n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
		{ 
            scanf("%d",&b[i]);
            if(a[i]==1&&b[i]==0) g[i][i]=1;//本班人能坐自己位置 
        }
        for(int i=1;i<=n;i++)
		{
            for(int j=1;j<=n;j++)
			{
                int x;scanf("%d",&x);
                if(x==1&&a[j]==1) g[i][j]=1;//j可以坐在i这个位置 
            }
        }
        bool flag=0;
        for(int i=1;i<=n;i++){
            memset(vis,0,sizeof(vis));
            if((!a[i]) || (a[i]&&!b[i])){//i这个人是外班的或分班前后依旧是本班的人都需要找个位置 
                if(!find(i)){
					flag=1;//有人没座,break 
					break;
                }
            }
        }
        printf("%s\n",flag?"T_T":"^_^");
    }
    return 0;
}

网络流解法

\(S\) 向每个座位连边,座位向能坐座位的人连边,人像 \(T\) 连边,边权均为一,最大流即为能找到座位的人数,与总座位数比较

即可,注意细节和上面二分图解法细节一致

题解——代码来自JD大佬
#include <bits/stdc++.h>
#define N 100005
#define inf 1e5

using namespace std;

int n,m,S,T,dis[N],sum,a[N],b[N],c[55][55],Q;
queue < int > q;

struct Edge{int next,to,dis,flow;}edge[N];
int head[N],now[N],cnt = 1;
void add(int from,int to,int dis){
	edge[++cnt] = (Edge){head[from],to,dis,0};
	head[from] = cnt;
}

void Input(){
	memset(head,0,sizeof(head));cnt = 1;
	scanf("%d",&n);S = 0,T = 2 * n + 1,sum = n;
	for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
	for(int i = 1;i <= n;i ++) {scanf("%d",&b[i]);if(a[i] == 1 and b[i] == 1) sum --;}
	for(int i = 1;i <= n;i ++) for(int j = 1;j <= n;j ++) scanf("%d",&c[i][j]);
	for(int i = 1;i <= n;i ++){
		for(int j = 1;j <= n;j ++){
			if((c[i][j] == 1 or i == j) and a[i] == 1) add(i,j + n,1),add(j + n,i,0); 
		}
	} 
	for(int i = 1;i <= n;i ++) if(a[i] == 1) add(S,i,1),add(i,S,0);
	for(int i = 1;i <= n;i ++) if(a[i] == 0 or b[i] == 0) add(i + n,T,1),add(T,i + n,0); 
}

bool bfs(){
	for(int i = S;i <= T;i ++) dis[i] = 0;
	dis[S] = 1,q.push(S);
	while(!q.empty()){
		int x = q.front();q.pop();
		now[x] = head[x];
		for(int i = head[x];i;i = edge[i].next){
			int y = edge[i].to;
			if(!dis[y] and edge[i].dis > edge[i].flow){
				dis[y] = dis[x] + 1;
				q.push(y);
			}
		}
	}
	return dis[T];
}

int dfs(int x,int flow){
	if(x == T or !flow) return flow;
	int res = 0;
	for(int i = now[x];i;i = edge[i].next){
		now[x] = i;
		int y = edge[i].to,d;
		if(dis[y] == dis[x] + 1 and (d = dfs(y,min(edge[i].dis - edge[i].flow,flow - res)))){
			edge[i].flow += d,edge[i ^ 1].flow -= d;
			res += d;if(res == flow) break;
		}
	}
	return res;
}

void work(){
	int ans = 0;
	while(bfs()) ans += dfs(S,inf);
	if(ans == sum) puts("^_^");
	else puts("T_T");
}

signed main(){scanf("%d",&Q);while(Q --){Input();work();}return 0;}
posted @ 2024-05-11 17:12  _君の名は  阅读(24)  评论(0编辑  收藏  举报