• luogu P3231 消毒


    这是一道二分图匹配的题目。

    我一开始想了很久可是毫无思路,只发现了一个性质:对于长宽高三个维度((A,B,C)),我们每次选择的消毒范围必然有两维是要选满的(因为是要取(min),所以这样取一定是最优的)。

    然后就不会了,点击查看题解

    首先考虑二维的情况:在一个(n)(m)列的矩阵里有若干个点,每次你可以覆盖一行或者一列,操作一次的费用为(1),求覆盖所有点的最小费用。

    我比较菜所以发现这个都不会做。。。

    做法是在二分图左边放(n)个点,右边放(m)个点,对于坐标为((x,y))的一个点,将其左边的第(x)号点和右边的第(y)号点相连,最后最大匹配就是答案。

    考虑为什么这样做的对的:显然一个点((x,y))要么被第(x)行覆盖要么被第(y)列覆盖,所以是我们建出来图的最小点覆盖,而最小点覆盖=最大匹配。

    然后再考虑本题,发现并没有什么三分图匹配(反正我是不会),同时发现(A imes B imes Cleq5000),也就是说(Aleq sqrt[3]{5000} leq 18)(假设(A leq B leq C)),所以我们可以拿出来一维暴搜它要不要选,然后就被拍扁成二维的形式啦。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    const int N=200009,INF=1<<30;
    queue <int> q;
    int A,B,C,S,T,a[N],b[N],c[N],s[4][N],qwq,ans,tmp,head[N],cnt,cnmb[N];
    int dep[N],cur[N];
    struct Edge
    {
    	int nxt,to,w;
    }g[N*2];
    
    int read()
    {
    	char c=getchar();
    	int x=0;
    	while(c<'0'||c>'9')
    		c=getchar();
    	while(c>='0'&&c<='9')
    		x=x*10+c-'0',c=getchar();
    	return x;
    }
    
    void add(int from,int to,int w)
    {
    	g[++cnt].nxt=head[from];
    	g[cnt].to=to;
    	g[cnt].w=w;
    	head[from]=cnt;
    }
    
    void ADD(int from,int to,int w)
    {
    	add(from,to,w),add(to,from,0);
    }
    
    void clear()
    {
    	for (int i=0;i<=T;i++)
    		dep[i]=-1,cur[i]=head[i];
    }
    
    bool bfs()
    {
    	clear(),q.push(S),dep[S]=0;
    	while(!q.empty())
    	{
    		int x=q.front();q.pop();
    		for (int i=head[x];i;i=g[i].nxt)
    		{
    			int v=g[i].to;
    			if(dep[v]!=-1||g[i].w<=0)
    				continue;
    			dep[v]=dep[x]+1;
    			q.push(v);
    		}
    	}
    	return dep[T]!=-1;
    }
    
    int dfs(int x,int Min)
    {
    	if(x==T)
    		return Min;
    	int flow=0;
    	for (int &i=cur[x];i;i=g[i].nxt)
    	{
    		int v=g[i].to;
    		if(dep[v]!=dep[x]+1||g[i].w<=0)
    			continue;
    		int tmp=dfs(v,min(Min-flow,g[i].w));
    		flow+=tmp,g[i].w-=tmp,g[i^1].w+=tmp;
    		if(flow==Min)
    			break;
    	}
    	return flow;
    }
    
    int Dinic()
    {
    	int ans=0;
    	while(bfs())
    		ans+=dfs(S,INF);
    	return ans;
    }
    
    void fuck(int x)
    {
    	cnt=1;
    	int tmp=0;
    	for (int i=1;i<=A;i++)
    		if(1<<(i-1)&x)
    			tmp++,cnmb[i]=1;
    		else
    			cnmb[i]=0;
    	T=B+C+1;
    	for (int i=0;i<=T;i++)
    		head[i]=0;
    	for (int i=1;i<=qwq;i++)
    		if(!cnmb[a[i]])
    			ADD(b[i],c[i]+B,1);
    	for (int i=1;i<=B;i++)
    		ADD(S,i,1);
    	for (int i=1;i<=C;i++)
    		ADD(i+B,T,1);
    	tmp+=Dinic();
    	ans=min(ans,tmp);
    }
    
    void init()
    {
    	A=read(),B=read(),C=read();
    	int x;
    	qwq=0;
    	for (int i=1;i<=A;i++)
    		for (int j=1;j<=B;j++)
    			for (int k=1;k<=C;k++)
    			{
    				x=read();
    				if(x)
    					s[1][++qwq]=i,s[2][qwq]=j,s[3][qwq]=k;
    			}
    	if(A>B)
    		swap(A,B),swap(s[1],s[2]);
    	if(A>C)
    		swap(A,C),swap(s[1],s[3]);
    	for (int i=1;i<=qwq;i++)
    		a[i]=s[1][i],b[i]=s[2][i],c[i]=s[3][i];
    	ans=INF;
    }
    
    void work()
    {
    	int T;
    	T=read();
    	while(T--)
    	{
    		init();
    		for (int i=0;i<1<<A;i++)
    			fuck(i);
    		printf("%d
    ",ans);
    	}
    }
    
    int main()
    {
    	work();
    	return 0;
    }
    
    由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!
  • 相关阅读:
    Python网络爬虫第三弹《爬取get请求的页面数据》
    18.增量式爬虫
    17.基于scrapy-redis两种形式的分布式爬虫
    关于进程内存磁盘的一些命令
    linux其他命令
    ls -用于显示指定工作目录下之内容(列出目前工作目录所含之文件及子目录)
    mkdir和touch
    ls -列出当前目录下所有的文件或者目录
    cat -用于连接文件并打印到标准输出设备上
    rm -移动文件
  • 原文地址:https://www.cnblogs.com/With-penguin/p/12698459.html
Copyright © 2020-2023  润新知