[状压dp][思维] Jzoj P6299 工厂

Description

 

题解

  • 首先先转换题目,就是n个工人和n台机器,转换为二分图模型,左右两点有边当且仅当该人会该机器
  • 无论哪种情况所有机器都有人操作,而且所有人都有事干,也就是每一个极大匹配都是完美匹配
  • 然后有结论为任意一个极大匹配都是完美匹配当且仅当二分图中每一个联通块都是左右点数相同的完全二分图
  • 证明如下:
  •  

  • 然后我们回到问题本身,那么记录一开始每个联通块左右点数为(xi,yi),现在的问题就是将(xi,yi)分成若干个集合,然后要满足Σi∈s(xi)=Σi∈s(yi)
  • 考虑状压dp,设f[s][i]为集合中s已经划分出Σx=i的最小代价,至于转移的话,枚举x从f[s]转移到f[s∪{x}]
  • 然后就可以过这道题了

 

代码

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 const int inf=168430090,N=40,M=223321;
 7 struct data{int x,y,num;}p[N];
 8 struct edge{int to,from;}e[N*N*2];
 9 int n,m,cnt,tot,sum,g[M][N],f[M][N],l[N],s[N],head[N*2];
10 char S[N]; bool bz[N*2],pd[M];
11 void insert(int x,int y)
12 {
13     e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt;
14     e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt;
15 }
16 void dfs(int x)
17 {
18     if (x<=n) p[tot].x++; else p[tot].y++; bz[x]=1;
19     for (int i=head[x];i;i=e[i].from) if (!bz[e[i].to]) dfs(e[i].to);
20 }
21 bool cmp(data a,data b) { return a.x==b.x?a.y<b.y:a.x<b.x; } 
22 void check(int x)
23 {
24     int r=x,a=0,b=0;
25     for (int i=m;i>=1;i--) while (r>=l[i]) g[x][i]++,a+=p[i].x,b+=p[i].y,r-=l[i];
26     pd[x]=(a==b);
27 }
28 int main()
29 {
30     freopen("factory.in","r",stdin),freopen("factory.out","w",stdout),scanf("%d",&n);
31     for (int i=1;i<=n;i++)
32     {
33         scanf("%s",S+1);
34         for (int j=1;j<=n;j++) if (S[j]=='1') sum++,insert(i,j+n);
35     }
36     for (int i=1;i<=n;i++) if (!bz[i+n]) tot++,dfs(i+n);
37     for (int i=1;i<=n;i++) if (!bz[i]) p[++tot]=(data){1,0,0};
38     sort(p+1,p+tot+1,cmp),m=p[1].num=1;
39     for (int i=2;i<=tot;i++) if (p[i].x!=p[i-1].x||p[i].y!=p[i-1].y) p[++m]=(data){p[i].x,p[i].y,1}; else p[m].num++;
40     l[1]=1,s[1]=p[1].num; for (int i=2;i<=m;i++) l[i]=s[i-1]+1,s[i]=s[i-1]+l[i]*p[i].num;
41     memset(f,10,sizeof(f)),f[0][0]=0;
42     for (int i=1;i<=m;i++) f[l[i]][p[i].x]=0;
43     for (int i=1;i<=s[m];i++) check(i);
44     for (int i=0;i<=s[m];i++) for (int j=1;j<=m;j++) if (p[j].num!=g[i][j]) for (int k=0;k<=n;k++) if (f[i][k]!=inf)
45     { 
46         f[i+l[j]][k+p[j].x]=min(f[i][k],f[i+l[j]][k+p[j].x]);
47         if (pd[i+l[j]]) f[i+l[j]][0]=min(f[i+l[j]][0],f[i+l[j]][k+p[j].x]+(k+p[j].x)*(k+p[j].x));
48     }
49     printf("%d",f[s[m]][0]-sum);
50 }

 

posted @ 2019-08-12 21:37  BEYang_Z  阅读(240)  评论(0编辑  收藏  举报