[CSP-Sシミュレーション試験]:(圧力DPのような)巨大なマジック

タイトル説明

OberthurですLeask巨大な魔法が有向グラフの非常に好きで、1日、彼は$ M $のエッジの有向グラフの$ n $のポイントを発見しました。
オベロイLeaskは有向グラフリングが美しいです信じていなかった、私はこの絵は美しいです持っているどのように多くの部分グラフ(すなわちエッジの選択セット)頼みますか?$ 1000000007 $剰余への答え。


入力形式

二つの整数のn $ $と$ M $の最初の行。
次の二つの行の各行$ M $有向エッジの整数を表します。ノー再ループフリーエッジいることを確認します。


出力フォーマット

$ 1000000007 $を法の整数の答えの行、。


サンプル

サンプル入力:

3 6
1 2
2 1
1 3
3 1
2 3
3 2

出力例:

25


データ範囲とヒント

$ 40 \%$データ$ n個の\ leqslant 5、m個の\のため leqslant $ 20、
$ 60 \%$データ$ n個の\ leqslant 10 $のために、
$ \ 80%$データ$ n個の\ leqslant 15 $のために、
$ \ 100用%$データの$ n \は17 $をleqslant。


問題の解決策

データ範囲が$ 17 $で参照してください、私はこの$ AK $をテストすることができると思います。私が見たときにしかし、それはエッジを設定する時間ですが、私は部屋で半時間後にはほとんどのキーボードの音を聞いてことがわかった......

同様に、我々は最初の練習の$ 60 \%$を考えます。

第一層の度合いが$ 0 $点で階層非循環有向グラフを与え、第2の層は、$ topsort $に類似度$ 0 $点の削除の第一の層は?

次に[I] [J] $ノードの現在選択されたセットを示す$のDPを設定は、$ iが、ノード$ J $のプログラム番号のセットの最終層を$です。

S_1が$ $ノードの現在選択されたセットを示していると仮定することができる、S_2 $ $は、ノードの集合の最後の層、S_3 $ $ $補体S_1、S_2および$ $ $の$および$ S_3連結側の部分集合、$を表しS_1 \ oplus S_2 $と$ S_3 $接続側は$ cnt_1 $バー、$ S_2 $ $ $ $ S_3 cnt_2 $ストリップの側に接続されています。

その後、状態遷移方程式は、次のとおり

$$ DP [S_1 | S_3] [S_3] = \和[S_1] [S_2] DP回\ \回\ PROD 2 ^ {cnt_1}(2 ^ {cnt_2} -1)$$

このような時間の複雑さは、$ \シータである$(4 ^ n回mを\)、のいずれかのスペースや時間が十分ではありません。

したがって、我々は、現在選択されているノードの直接の列挙は、その補数のサブセットを列挙し、その後$ S_2 $、$ S_1 $を設定し、さらに$ S_1 $ $ $ S_2 $側面が持っている間で設定二次元を引く考えますCNT $ストリップ。

[| S_2 S_1] = DP [S_1] \回2 ^ {CNT} $ $のDP:さて、あなたは、遷移方程式のような状態で表示されることがあります。

S_2 $は$ S_1 $と$ S_2構成$多くのことなので、我々はまた、係数$との包含と除外を必要とする( - 1)^ {SZ [S] 1 |しかし、あなたは答えは限り$ S_1をカウントすることがわかります} $。

$ \シータ(3 ^ nm)の$のために、この時間の時間複雑さは、たったの$、80 $の点は、また、最適化がたくさんある、最適化する必要があります、私は$ \シータ(2 ^ n個の\ n倍^ 2にそれを最適化することができます)$、あなたはAC $を$できるように、この質問です。

時間複雑:$ \シータ(2 ^ n個の\ n倍^ 2)$。

期待はスコア:$ $ 100ポイントを。

実際のスコア:$ $ 100ポイント。


コードの時間

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n,m;
bool Map[20][20];
int sz[131073];
int du[65537];
int sum[131073];
int qpow[1000];
int dp[131073];
int main()
{
	scanf("%d%d",&n,&m);
	int maxn=(1<<n);
	dp[0]=qpow[0]=1;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		Map[x-1][y-1]=1;
		qpow[i]=(qpow[i-1]<<1)%mod;
	}
	sz[0]=-1;
	for(int i=1;i<maxn;i++)sz[i]=sz[i>>1]*(i&1?-1:1);
	for(int i=0;i<maxn-1;i++)
	{
		if(!dp[i])continue;
		int t=maxn-1-i;
		memset(du,0,sizeof(du));
		for(int j=0;j<n;j++)
			if(i&(1<<j))
				for(int k=0;k<n;k++)
					du[1<<k]+=Map[j][k];
		sum[0]=0;
		for(int s=(t-1)&t;;s=(s-1)&t)
		{
			int now=t^s,lst=now&-now;
			sum[now]=sum[now-lst]+du[lst];
			dp[i+now]=(dp[i+now]+1LL*sz[now]*qpow[sum[now]]*dp[i])%mod;
			if(!s)break;
		}
	}
	printf("%lld",dp[maxn-1]);
	return 0;
}

rp++

おすすめ

転載: www.cnblogs.com/wzc521/p/11619687.html