zoukankan      html  css  js  c++  java
  • JLOI2015 战争调度【树形dp】

    题目链接

    题目解析

    乍一想有(2^{2^n})种状态,直接暴力不太可取。

    考虑树(dp)的话,设(dp[i])表示子树(i)的答案,由于有个(m)的限制,我们设(dp[i][j])表示子树(i)内有(j)个人选择作战的最大贡献。

    但是这样很难转移,因为我们不知道儿子们选了啥。

    但如果从儿子们的角度来考虑,若一个叶结点的所有祖先结点的状态确定,那么这个叶结点的贡献可以直接算出来。而每个叶结点有(2^{n-1})个祖先,如果枚举它们的祖先的状态,复杂度消耗只有(2^{n-1}),再乘上叶结点个数,时间复杂度是(2^{n-1} imes 2^{n-1}=2^n)

    所以相当于可以枚举一条链上的每个点的状态,用类似于状压的方式存状态,然后到了叶子结点可以转移上去。

    (我在想这样枚举复杂度不是狂野的(2^{2^n})应该是因为,它是按照父子关系枚举下来的,按照每一条链的顺序枚举下来,左右儿子通过(dp)的方式合并答案,而不是像直接暴力那样,链与链之间通过枚举状态计算答案,(有点像背包那个意思),把链与链之间的复杂度从加法变成了乘法)

    每个点还有转移(dp[u][i+j]=max(dp[u<<1][i]+dp[u<<1|1][j])),这里还有个(n)的复杂度,所以总时间复杂度应该是(O(n2^n))


    ►Code View

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
    using namespace std;
    #define LL long long
    #define N 15
    #define K 5000
    #define INF 0x3f3f3f3f
    int rd()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
    	return f*x;
    }
    int n,m,k,ans;
    int w[K][N],f[K][N];//war farm 
    int dp[K][K];//以i为根的子树内有j个平民作战的最大贡献 
    void dfs(int u,int s,int d)//u为子树根节点 s为祖先的状态(状压 1作战 0后勤) d为子树大小 
    {
    	for(int i=0;i<=d;i++)
    		dp[u][i]=0;
    	//记得清空 下面的dp值在根选另一个选项时算过 
    	if(d==1)
    	{//叶子结点 
    		for(int i=0;i<n-1;i++)
    		{//枚举n-1个祖先 高位是比较老的祖先 爸爸是最低位 
    			if((s>>i)&1)//第i个祖先作战 
    				dp[u][1]+=w[u-(k-1)][i+1]; 
    			else dp[u][0]+=f[u-(k-1)][i+1];
    		}
    		return ;
    	}
    	for(int sit=0;sit<=1;sit++)
    	{//枚举当前为作战/后勤 
    		dfs(u<<1,(s<<1)|sit,d>>1);
    		dfs(u<<1|1,(s<<1)|sit,d>>1);
    		for(int i=0;i<=min(d,m);i++)
    			for(int j=0;j<=min(d,m);j++)
    				dp[u][i+j]=max(dp[u][i+j],dp[u<<1][i]+dp[u<<1|1][j]); 
    	}
    } 
    int main()
    {
    	n=rd(),m=rd();
    	k=1<<(n-1);//叶子结点个数 
    	for(int i=1;i<=k;i++)
    		for(int j=1;j<=n-1;j++)
    			w[i][j]=rd();
    	for(int i=1;i<=k;i++)
    		for(int j=1;j<=n-1;j++)
    			f[i][j]=rd();
    	dfs(1,0,(1<<n)-1);
    	for(int i=0;i<=m;i++)
    		ans=max(ans,dp[1][i]);
    	printf("%d
    ",ans); 
    	return 0;
    }
    
  • 相关阅读:
    exkmp
    欧拉通路、回路
    你有多久没有看过星星
    trie树模板(统计难题)
    SPFA(热浪)
    codevs1958 刺激
    洛谷1290 欧几里得的游戏
    洛谷1016 旅行家的预算
    Spfa算法模板
    Tyvj2017清北冬令营入学测试
  • 原文地址:https://www.cnblogs.com/lyttt/p/14011601.html
Copyright © 2011-2022 走看看