• 【BZOJ3140】消毒(HNOI2013)-二分图匹配


    测试地址:消毒
    做法: 本题需要用到二分图匹配。
    挺妙的一道题,我反正是没想出来。首先考虑二维的情况,即二维平面中有一些点,覆盖一个x×yx imes y的矩形所需要的代价是min(x,y)min(x,y),求最小代价。
    我们发现,直接覆盖一个x×yx imes y的矩形,并不比直接覆盖min(x,y)min(x,y)条横行或纵列优,因此最优解中一定是用一些横行和纵列覆盖,求最小代价就是很经典的二分图最小点覆盖问题了,转化成求最大匹配即可。
    但是现在有三维,用类似的讨论可以将问题转化为,用三种平面去覆盖这些点(这里平面指的就是某一维长度为11,另外两维无限长的立方体),但我们发现这个问题不能直接转化成二分图问题,而其他图的覆盖问题又是NP的,所以我们只能考虑枚举其中一种平面的选取。因为abc5000acdot bcdot cle 5000,所以min(a,b,c)17min(a,b,c)le 17,因此枚举最小那一维为11的平面的选取情况有2172^{17}种。令最小的这一维为aa,枚举完这个后,我们发现另外两种平面中,aa这一维都是无限长的,因此我们直接不管aa坐标,而只考虑b,cb,c坐标的覆盖问题,这就是一个二维问题了,因此就用上面的二分图匹配解决即可。复杂度比较玄幻,但原数据还是能轻松过的,BZOJ新增的数据需要卡点常。
    (据加强数据的dalao说,有更优的,稳过的做法,但鉴于现在找不到这位大佬,所以只能这样了,或者使用Dinic这样的网络流做法来优化可能也行?)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int T,a,b,c,totp,l[20],r[20];
    int first[10010],mat[10010],tot,tim=0;
    struct point
    {
    	int x,y,z;
    }p[5010];
    struct edge
    {
    	int v,next;
    }e[5010];
    int vis[5010]={0};
    
    void init()
    {
    	totp=0;
    	
    	scanf("%d%d%d",&a,&b,&c);
    	for(int i=1;i<=a;i++)
    		for(int j=1;j<=b;j++)
    			for(int k=1;k<=c;k++)
    			{
    				int x;
    				scanf("%d",&x);
    				if (x) p[++totp].x=i,p[totp].y=j,p[totp].z=k;
    			}
    	if (a>b)
    	{
    		swap(a,b);
    		for(int i=1;i<=totp;i++)
    			swap(p[i].x,p[i].y);
    	}
    	if (a>c)
    	{
    		swap(a,c);
    		for(int i=1;i<=totp;i++)
    			swap(p[i].x,p[i].z);
    	}
    	if (b>c)
    	{
    		swap(b,c);
    		for(int i=1;i<=totp;i++)
    			swap(p[i].y,p[i].z);
    	}
    }
    
    bool cmp(point a,point b)
    {
    	return a.x<b.x;
    }
    
    void insert(int a,int b)
    {
    	e[++tot].v=b;
    	e[tot].next=first[a];
    	first[a]=tot;
    }
    
    int dfs(int v,int now)
    {
    	if (vis[v]==now) return 0;
    	vis[v]=now;
    	for(int i=first[v];i;i=e[i].next)
    		if (!mat[e[i].v]||dfs(mat[e[i].v],now))
    		{
    			mat[e[i].v]=v;
    			return 1;
    		}
    	return 0;
    }
    
    void work()
    {
    	sort(p+1,p+totp+1,cmp);
    	for(int i=1;i<=a;i++)
    		l[i]=r[i]=0;
    	for(int i=1;i<=totp;i++)
    	{
    		if (!l[p[i].x]) l[p[i].x]=i;
    		r[p[i].x]=i;
    	}
    	
    	int finalans=1000000000;
    	for(int i=0;i<(1<<a);i++)
    	{
    		int x=i,ans=0;
    		for(int j=1;j<=b;j++)
    			first[j]=0;
    		for(int j=1;j<=c;j++)
    			mat[j]=0;
    		tot=0;
    		while(x)
    		{
    			if (x&1) ans++;
    			x>>=1;
    		}
    		
    		for(int j=1;j<=totp;j++)
    		{
    			if (i&(1<<(p[j].x-1))) continue;
    			insert(p[j].y,p[j].z);
    		}
    		for(int j=1;j<=b;j++)
    		{
    			ans+=dfs(j,++tim);
    			if (ans>=finalans) break;
    		}
    		finalans=min(finalans,ans);
    	}
    	printf("%d
    ",finalans);
    }
    
    int main()
    {
    	scanf("%d",&T);
    	while(T--)
    	{
    		init();
    		work();
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    《算法》第六章部分程序 part 5
    《算法》第六章部分程序 part 4
    《算法》第六章部分程序 part 3
    《算法》第六章部分程序 part 2
    《算法》第六章部分程序 part 1
    OpenGL Hello World
    《算法》第五章部分程序 part 3
    《算法》第五章部分程序 part 8
    《算法》第五章部分程序 part 7
    《算法》第五章部分程序 part 6
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793247.html
Copyright © 2020-2023  润新知