zoukankan      html  css  js  c++  java
  • 【洛谷P3349】[ZJOI2016]小星星

    【洛谷传送门】
    借鉴了 (mathtt{wind\_whisper}) 的思路,他的【博客传送门】(不过我的代码写的比他好看多了)

    题解

    先不提及优化,一开始本人并没有设计出朴素 DP。(或许有些浮躁?)

    设计 DP

    看到数据范围还有题目里面玄学的对应关系,可以想到状压,用一维表示已被对应的节点状态。
    考虑对于每一个转移的父子点对 (u,v),由于转移的条件就是有连边,所以 (u,v) 对应的点在原图中一定有连边,转移的时候把 (u) 当前子树和 (v) 子树的状态合并(也就是逻辑或),就可以顺利转移。
    因此,设计 (dp(u,stat,p)) 表示 (u) 节点对应的节点是 (p)(u) 子树内对应状态为 (stat)

    优化 DP

    可以用容斥优化,但是比较难。(先挖坑)

    • 最简单的想法,基于 DP 本身的状态,每个子树对应的状态里 (1) 的个数一定等于子树大小,预处理出来可以省去大量枚举。
    • 其次,枚举子树的时候类比树形背包,每次转移完 DP 之后再 siz[u]+=siz[v]
    • 基本就这些,还可以有一点点无聊的优化,比如在 DP 值为 (0) 的时候直接跳出循环。

    吸氧能够卡过,最大点约 (850ms)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int INF = 0x3f3f3f3f,N = 20;
    inline ll read()
    {
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
    	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    int n,m,head[N],ecnt=-1;
    bool mp[N][N];
    inline void init_edge(){memset(head,-1,sizeof(head)),ecnt=-1;}
    struct edge
    {
    	int nxt,to;	
    }a[N<<1];
    inline void add_edge(int x,int y)
    {
    	a[++ecnt]=(edge){head[x],y};
    	head[x]=ecnt;
    }
    ll dp[N][1<<18][N],siz[N];
    int num[N][1<<18],cnt[N];
    void dfs(int u,int fa)
    {
    	siz[u]=1;
    	for(int i=1;i<=n;i++) dp[u][1<<(i-1)][i]=1LL;
    	//初始化,u对应任何一个点方案都为1
    	for(int i=head[u];~i;i=a[i].nxt)
    	{
    		int v=a[i].to;
    		if(v==fa) continue; 
    		dfs(v,u);
    		for(int j=1;j<=cnt[siz[u]];j++)	
    			for(int k=1;k<=cnt[siz[v]];k++)
    			{
    				int stat1=num[siz[u]][j];
    				int stat2=num[siz[v]][k];
    				if(stat1&stat2) continue;
    				for(int p=1;p<=n;p++)
    					if((1<<(p-1))&stat2)
    					{
    						if(!dp[v][stat2][p]) continue;
    						for(int q=1;q<=n;q++)
    						{
    							if(mp[q][p]&& ((1<<(q-1))&stat1) )
    							dp[u][stat1|stat2][q]+=dp[u][stat1][q]*dp[v][stat2][p];
    						}
    					}
    			}
    		siz[u]+=siz[v];
    	}
    }
    int main()
    {
    	init_edge();
    	n=read(),m=read();
    	for(int i=1;i<=m;i++)	
    	{
    		int u=read(),v=read();
    		mp[u][v]=mp[v][u]=1;
    	}
    	for(int i=1;i<n;i++)	
    	{
    		int u=read(),v=read();
    		add_edge(u,v),add_edge(v,u);
    	}
    	for(int i=0;i<1<<n;i++)	
    	{
    		int tcnt=0;
    		for(int j=1;j<=n;j++)	
    			if((1<<(j-1))&i) tcnt++;
    		num[tcnt][++cnt[tcnt]]=i;
    	}
    	dfs(1,-1);
    	ll ans=0ll;
    	for(int i=1;i<=n;i++)		
    		ans+=dp[1][(1<<n)-1][i];
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    pd_ds中的hash
    洛谷P1333 瑞瑞的木棍(欧拉回路)
    2-SAT速成
    洛谷P3209 [HNOI2010]PLANAR(2-SAT)
    BZOJ2199: [Usaco2011 Jan]奶牛议会(2-SAT)
    BZOJ 1823: [JSOI2010]满汉全席(2-SAT)
    IOS深入学习(1)之图标文件(icon files)
    控制反转思想的理解实例
    poj 3259 (Bellman_Ford判断负环)
    POJ 2686 Traveling by Stagecoach 壮压DP
  • 原文地址:https://www.cnblogs.com/conprour/p/15484891.html
Copyright © 2011-2022 走看看