• 【PKUWC2018】随机算法


    Description

    我们知道,求任意图的最大独立集是一类NP完全问题,目前还没有准确的多项式算法,但是有许多多项式复杂度的近似算法。

    例如,小 C 常用的一种算法是:

    1.对于一个 (n) 个点的无向图,先等概率随机一个 (1ldots n) 的排列 (p[1ldots n])

    2.维护答案集合 (S) ,一开始 (S) 为空集,之后按照 (i=1ldots n)的顺序,检查 ({p[i]}cup S) 是否是一个独立集,如果是的话就令 (S={p[i]}cup S)

    3.最后得到一个独立集 (S) 作为答案。

    小 C 现在想知道,对于给定的一张图,这个算法的正确率,输出答案对 (998244353) 取模

    Input

    第一行两个非负整数 (n,m) 表示给定的图的点数和边数。

    接下来 (m) 行,每行有两个正整数 ((u,v) (u eq v))描述这张图的一条无向边。

    Output

    输出正确率,答案对 (998244353) 取模。

    Solution

    看到(n leq 20) 时,有经验的选手应该就会往状压DP上去想.

    (F_{i,j})表示当前独立集大小为i,不能选的点集为j的方案数,(S_u)表示选了(i)后不能选的点的集合,易得

    [F_{i,jigcup S_u}=F_{i,jigcup S_u}+F_{i-1,j}*A_{n-mid j mid -1}^{mid jigcup S_u mid - mid j mid -1 } u otin j ]

    复杂度 (O(2^n * n^2)),这看起来已经很优秀了。

    继续观察,发现每个状态的最大独立集是唯一的

    (S_j)表示不能选的点集为j时最大的独立集大小

    每次更新(S_j)时将(F_j)清零即可

    复杂度变为(O(2^n*n))

    Code

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int Mod=998244353;
    int n,m,cou[1500000],state[50],num[1500000];
    long long J[50],dp[1500000],R[50];
    long long fpow(long long a,int k)
    {
    	long long ans=1;
    	while (k)
    	{
    		if (k&1) ans=ans*a%Mod;
    		a=a*a%Mod;
    		k>>=1;
    	}
    	return ans;
    }
    long long A(int n,int m)
    {
    	return (n>=m?J[n]*R[n-m]%Mod:0);
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<(1<<n);i++) cou[i]=cou[i>>1]+(i&1);
    	for (int i=1;i<=n;i++) state[i]|=1<< i-1;
    	for (int i=1;i<=m;i++)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		state[u]|=1<< v-1;
    		state[v]|=1<< u-1;
    	}
    	J[0]=1;
    	for (int i=1;i<=n;i++) J[i]=J[i-1]*i%Mod;
    	for (int i=0;i<=n;i++) R[i]=fpow(J[i],Mod-2);
    	dp[0]=1;num[0]=0;
    	for (int i=0;i<(1<<n);i++)
    	{
    		if (!dp[i]) continue;
    		for (int j=1;j<=n;j++)
    		{
    			if ((i>>(j-1)) & 1) continue;
    			if (num[i]+1>num[i|state[j]]) num[i|state[j]]=num[i]+1,dp[i|state[j]]=0;
    			if (num[i]+1==num[i|state[j]]) dp[i|state[j]]=(dp[i|state[j]]+dp[i]*A(n-cou[i]-1,cou[i|state[j]]-cou[i]-1)%Mod)%Mod;
    		}
    	}
    	printf("%lld
    ",dp[(1<<n)-1]*fpow(J[n],Mod-2)%Mod);
    	return 0;
    }
    
  • 相关阅读:
    sql中的group by 和 having 用法解析
    关于js的this上下文环境绑定
    windows Service循环任务.服务启动后无法停止重启的解决办法
    js 数组对象的操作方法
    SVN Client API的.net 接口 SharpSvn介紹 Checkout操作实例
    JQuery实现表格的相同单元格合并的三种方法
    SharpSVN出错信息:Can't determine the user's config path,从此证明了百度是个垃圾
    显示js对象所有属性和方法的函数
    软件开发实践的24条军规
    C#中的泛型
  • 原文地址:https://www.cnblogs.com/Code-Geass/p/10291705.html
Copyright © 2020-2023  润新知