zoukankan      html  css  js  c++  java
  • Wannafly挑战赛26-F-msc的棋盘[最小割转化dp]

    题意

    一个大小为 (n*m) 的棋盘,知道每一列放了多少棋子,求有多少摆放方案满足要求。

    (n,mleq 50) .

    分析

    • 如果是求是否有方案的话可以考虑网络流,行列连边,列容量为 (b_j),行容量为 (m)

    • 考虑转化成一个最小割问题,假设(S ightarrow row)(i) 条边,(column ightarrow T)(j) 条边,中间显然要断开 ((n-i)*(m-j))条边。在这样的情况下左边和右边的边一定是前 (i) 小和前 (j) 小的边。

    • 发现只要最后 (sum{a_i}=sum{b_i}) ,且对于排序后的 (a) 任意的 (i) 满足(s_i=sum_{k=1}^{i}{a_i}) 都比其下限大就一定是合法方案。

    • 下限 (low)(s_i) 在所有的 (j) 的情况下的最大值。如果小于下限就会出现比 (sum{b_i}) 还要小的割,不符合题意。

    • 考虑定义状态 (f_{i,j,k}) 表示权值不超过 (i),选了 (j) 行,和为 (j) 的方案数。

    • 转移枚举 (i) 选择了 (x) 个,然后在剩下的行中选择 (x) 行的方案数为 (inom{n-j}{x}) ,要保证对于 (pin [0,x]),有 ((k+p*x)geq {low}_{j+p})

    • 总时间复杂度为 (O(n^3m^2))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].last,v=e[i].to)
    #define rep(i,a,b) for(int i=a;i<=b;++i)
    #define pb push_back
    typedef long long LL;
    inline int gi(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch))	{if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
    	return x*f;
    }
    template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
    template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
    const int N=54,inf=0x3f3f3f3f,mod=1e9 + 7;
    int n,m,sum;
    int b[N],a[N],c[N][N],f[N][N][N*N];
    void add(int &a,int b){a+=b;if(a>=mod) a-=mod;}
    int main(){
    	n=gi(),m=gi();
    	rep(i,1,m) b[i]=gi(),sum+=b[i];
    	sort(b+1,b+1+m);
    	rep(i,1,m) b[i]+=b[i-1];
    	
    	rep(i,1,n){
    		int tmp=inf;
    		rep(j,1,m) Min(tmp,(n-i)*(m-j)+b[j]);
    		a[i]=sum-tmp;
    	}
    	rep(i,0,n){
    		c[i][0]=1;
    		rep(j,1,i) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    	}
    	
    	f[0][0][0]=1;
    	rep(i,0,m)
    	rep(j,0,n)
    	rep(k,0,sum){
    		for(int v=0;j+v<=n&&k+i*v<=sum;++v){
    			if(a[j+v]>k+i*v) break;
    			add(f[i+1][j+v][k+i*v],1ll*f[i][j][k]*c[n-j][v]%mod);
    		}
    	}
    	printf("%d
    ",f[m+1][n][sum]);
    	return 0;
    }
    
  • 相关阅读:
    C#中将全部代码一次性折叠
    C#中图片单击旋转事件
    块参照重命名
    补强圈设计
    c# winform 按名称取得控件
    获得某控件的父控件(容器)中的所有控件
    回车键当Tab键使用
    替换CAD中原有命令为开发人员自己开发的命令的方法
    窗体设置
    判断控件的tag是否为空的方法
  • 原文地址:https://www.cnblogs.com/yqgAKIOI/p/9833346.html
Copyright © 2011-2022 走看看