zoukankan      html  css  js  c++  java
  • JZOJ 6299. 2019.08.12【NOIP提高组A】工厂(二分图+状压DP)

    JZOJ 6299. 2019.08.12【NOIP提高组A】工厂

    题目

    Description

    在这里插入图片描述

    Input

    在这里插入图片描述

    Output

    在这里插入图片描述

    Sample Input

    Sample 1:
    2
    11
    10

    Sample 2:
    2
    10
    00

    Sample 3:
    3
    000
    110
    000

    Sample Output

    Sample 1:
    1

    Sample 2:
    1

    Sample 3:
    3

    Data Constraint

    在这里插入图片描述

    Hint

    在这里插入图片描述

    题解

    • 先来看看题目背景,类似二分图匹配,
    • 任意极大匹配(随机顺序随机选取直到不能再选)后,最终都能得到完美匹配 n n n个人和 n n n台机器能分别相互匹配),才是符合题意的,
    • 不难发现,二分图分成的若干联通块,当且仅当它们左右点数相等,并且两两均有连边,才是符合条件的。
    • 设分成的第 i i i个联通块大小分别为 s [ i ] s[i] s[i],则最终连边总数 s u m = ∑ ∀ i s [ i ] 2 sum=sum_{forall i} s[i]^2 sum=is[i]2,那么答案即为 s u m − ∑ i = 1 n ∑ j = 1 n e [ i ] [ j ] sum-sum_{i=1}^{n}sum_{j=1}^{n}e[i][j] sumi=1nj=1ne[i][j],其中 e [ i ] [ j ] = 0 / 1 e[i][j]=0/1 e[i][j]=0/1,表示第 i i i个工人是否会操作第 j j j台机器,
    • 由于后面减去的值是固定的,所以只需要最小化 s u m sum sum的值,则可以取到最优的答案。
    • 设原图中分成的每个联通块 A [ i ] A[i] A[i],是由 x [ i ] x[i] x[i]个工人及 y [ i ] y[i] y[i]台机器组成,考虑状压DP,
    • f [ s ] [ i ] f[s][i] f[s][i]表示已选的联通块集合表示为 s s s,左边还剩 i i i个点还未匹配的最小边数,
    • s x sx sx为集合 s s s x [ i ] x[i] x[i]总和, s y sy sy同理,
    • 每次枚举一个新加入的联通块 k k k
    • 直接转移就 f [ s ] [ i ] f[s][i] f[s][i]更新 f [ s ⋃ k ] [ i + x [ k ] ] f[sigcup k][i+x[k]] f[sk][i+x[k]],这里不需要加入新的花费,
    • 直到当前剩余的与新加的可以组成一个左右点数相等的联通块,也就是 s x + x [ k ] = s y + y [ k ] sx+x[k]=sy+y[k] sx+x[k]=sy+y[k]时,
    • f [ s ] [ i ] + ( i + x [ k ] ) 2 f[s][i]+(i+x[k])^2 f[s][i]+(i+x[k])2更新 f [ s ⋃ k ] [ 0 ] f[sigcup k][0] f[sk][0]
    • s u m sum sum的最小值为 f [ S ] [ 0 ] f[S][0] f[S][0] S S S为所有联通块的集合。
    • 但这样的时间复杂度是 O ( 2 n ∗ n 2 ) O(2^n*n^2) O(2nn2)的,过不了,
    • 发现第一维的状态 s s s可能会有很多浪费,对于最初的联通块假设有 ( x [ i ] , y [ i ] ) = ( x [ j ] , y [ j ] ) (x[i],y[i])=(x[j],y[j]) (x[i],y[i])=(x[j],y[j]),它们其实可以看作是等价的,
    • 当有很多组 ( x , y ) (x,y) (x,y)相同时,可以把它们压成同样的一组;若有许多不同,那么总联通块数就会远远小于 n n n
    • 于是仍然设状态 f [ s ] [ i ] f[s][i] f[s][i],唯一与上面不同的是, s s s在这里不是二进制,而是一种每一位进制不同的数,表示的是每一种联通块分别选了多少个( 0 − c n t [ i ] 0-cnt[i] 0cnt[i]),这样 s s s的最大取值是小于 2 ∗ 1 0 5 2*10^5 2105的,可以极大地优化。
    • 转移类似,需要预处理前缀积。(具体可见处理类似的JZOJ 5831. 【NOIP提高A组模拟2018.8.18】 number或代码)

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,s=0,b1[31],b2[31],p[31][31];
    int sum[31][31],g[31],f[200010][31],c[31];
    char st[31];
    struct
    {
    	int x,y,s;
    }a[31];
    int sqr(int x)
    {
    	return x*x;
    }
    void dfs(int k,int o)
    {
    	if(o==1)
    	{
    		a[s].x++;
    		for(int i=1;i<=n;i++) if(p[k][i]&&!b2[i]) 
    		{
    			b2[i]=1;
    			dfs(i,2);
    		}
    	}
    	else
    	{
    		a[s].y++;
    		for(int i=1;i<=n;i++) if(p[i][k]&&!b1[i])
    		{
    			b1[i]=1;
    			dfs(i,1);
    		}
    	}
    }
    int main()
    {
    	int i,j,k;
    	scanf("%d
    ",&n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%s
    ",st+1);
    		for(j=1;j<=n;j++) p[i][j]=st[j]-'0';
    	}
    	for(i=1;i<=n;i++) if(!b1[i]) 
    	{
    		s++;
    		b1[i]=1;
    		dfs(i,1);
    	}
    	for(i=1;i<=n;i++) if(!b2[i])
    	{
    		s++;
    		b2[i]=1;
    		dfs(i,2);
    	}
    	for(i=1;i<=s;i++) sum[a[i].x][a[i].y]++;
    	s=0;
    	for(i=0;i<=n;i++)
    		for(j=0;j<=n;j++) if(sum[i][j]) a[++s].x=i,a[s].y=j,a[s].s=sum[i][j];
    	g[0]=1;
    	for(i=1;i<=s;i++) g[i]=g[i-1]*(a[i].s+1);
    	for(i=0;i<=g[s];i++)
    		for(j=0;j<=n;j++) f[i][j]=1e+9;
    	f[0][0]=0;
    	for(i=0;i<g[s];i++)
    	{
    		int t=i,tx=0,ty=0;
    		for(j=s;j;j--) 
    		{
    			c[j]=0;
    			while(t>=g[j-1]) c[j]++,t-=g[j-1],tx+=a[j].x,ty+=a[j].y;
    		}
    		for(j=0;j<=n;j++) if(f[i][j]<1e+9)
    		{
    			for(k=1;k<=s;k++) if(c[k]<a[k].s)
    			{
    				if(f[i][j]<f[i+g[k-1]][j+a[k].x]) f[i+g[k-1]][j+a[k].x]=f[i][j];
    				if(tx+a[k].x==ty+a[k].y&&f[i][j]+sqr(j+a[k].x)<f[i+g[k-1]][0]) f[i+g[k-1]][0]=f[i][j]+sqr(j+a[k].x);
    			}	
    		}
    	}
    	int ans=0;
    	for(i=1;i<=n;i++)
    		for(j=1;j<=n;j++) ans-=p[i][j];
    	printf("%d
    ",ans+f[g[s]-1][0]);
    	return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    Ocelot一款.NET下的API网关介绍
    【Core】.NET Core 部署( Docker + CentOS)
    VS2019添加git源代码管理
    sql两个表组合到一起,字符串拼接后放在最后一列上
    sqlserver取随机数随机取数
    delphi时间戳(10位)
    消息 7356,级别 16,状态 1,第 1 行 链接服务器 "downloadschoolcardinfo" 的 OLE DB 访问接口 "OraOLEDB.Oracle" 为列提供的元数据不一致。对象 ""VIEW_ZJK"."V_QDXQHIS_RYXX"" 的列 "XZZ" (编译时序号为 9)在编译时有 1 的 "LENGTH",但在运行时有 2。
    sqlserver简单的组合串
    Delphi提示:List Index out Of bounds(5)
    SQLserver简单的竖向转横向
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910067.html
Copyright © 2011-2022 走看看