zoukankan      html  css  js  c++  java
  • [JZOJ6299] 2019.08.12【NOIP提高组A】工厂

    题目

    题目大意

    工厂内每个人只会操作一些机器。
    他们会以随机的顺序来,每次选任意一台机器来操作。
    一台机器只能由一个工人来操作。
    可以花费一的代价来使某个工人学会一种机器。
    问花费最少的代价,使得在所有的情况下每个人都能操纵一台机器。


    正解

    这题可以转化成个二分图。而答案一定满足:所有联通块都是个完全二分图
    我们要用最少的代价来造出这样的二分图。
    预处理出所有的联通块,每个联通块用((x,y))表示,意味着左边有(x)个,右边有(y)个。
    于是就有了下面这个状压DP:(f_{S,i})表示(S)集中的联通块都被使用了,其中完成了的联通块的(x)之和为(i)的最小边数。
    等等,这个状态会不会存不下?
    实际上,对于相同的((x,y)),我们只关心数量,所以可以进一步压缩。然后状态的数量就变得少了很多。
    最后的答案记得要减去原来就有的边数。


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 33
    inline void update(int &x,int y){x>y?x=y:0;}
    int n;
    char can[N][N];
    bool vis[N][2];
    int cntl,cntr,have;
    void find(int x,bool k){
    	vis[x][k]=1;
    	if (k==0){
    		cntl++;
    		for (int i=1;i<=n;++i)
    			if (can[x][i]=='1' && !vis[i][1])
    				find(i,1);
    	}
    	else{
    		cntr++;
    		for (int i=1;i<=n;++i)
    			if (can[i][x]=='1' && !vis[i][0])
    				find(i,0);
    	}
    }
    int sc[N][N];
    struct State{
    	int x,y;
    	int num;
    } lis[N];
    int m;
    int pro[N];
    int chose[N];
    int f[200000][N];
    void dfs(int k,int s,int x,int y){
    	if (k>m){
    		if (s==0)
    			f[0][0]=0;
    		for (int j=0;j<n;++j){
    			if (x==y)
    				update(f[s][x],f[s][j]+(x-j)*(x-j));
    			for (int i=1;i<=m;++i)
    				if (chose[i]<lis[i].num)
    					update(f[s+pro[i-1]][j],f[s][j]);
    		}
    		return;
    	}
    	for (int i=0;i<=lis[k].num;++i){
    		chose[k]=i;
    		dfs(k+1,s+i*pro[k-1],x+i*lis[k].x,y+i*lis[k].y);
    	}
    }
    int main(){
    	freopen("factory.in","r",stdin);
    	freopen("factory.out","w",stdout);
    	scanf("%d",&n);
    	for (int i=1;i<=n;++i){
    		scanf("%s",can[i]+1);
    		for (int j=1;j<=n;++j)
    			if (can[i][j]=='1')
    				have++;
    	}
    	for (int i=1;i<=n;++i){
    		if (!vis[i][0]){
    			cntl=cntr=0;
    			find(i,0);
    			sc[cntl][cntr]++;
    		}
    		if (!vis[i][1]){
    			cntl=cntr=0;
    			find(i,1);
    			sc[cntl][cntr]++;
    		}
    	}
    	pro[0]=1;
    	for (int i=0;i<=n;++i)
    		for (int j=0;j<=n;++j)
    			if (sc[i][j]){
    				lis[++m]={i,j,sc[i][j]};
    				pro[m]=pro[m-1]*(sc[i][j]+1);
    			}
    	memset(f,127,sizeof f);
    	f[0][0]=0;
    	dfs(1,0,0,0);
    	printf("%d
    ",f[pro[m]-1][n]-have);
    	return 0;
    }
    

    总结

    居然还有如此鬼畜的DP……

  • 相关阅读:
    1.2 C++命名空间(namespace)
    1.3 C++引用(Reference)
    在ros功能包CMakeLists.txt中获取所在功能包的路径 便于添加第三方库的相对路径
    ubuntu14.04下搜狗输入法不能输入中文问题解决
    js对日期的判断
    Calendar用法随笔
    键盘事件
    onkeyup+onafterpaste 只能输入数字和小数点--转载
    导出数据到EXL表格中
    DENON AVR-X510BT 功放设置记录
  • 原文地址:https://www.cnblogs.com/jz-597/p/11421062.html
Copyright © 2011-2022 走看看