zoukankan      html  css  js  c++  java
  • 5.29 省选模拟赛 树的染色 dp 最优性优化

    LINK:树的染色

    avatar
    avatar

    考场上以为这道题要爆蛋了 没想到 推出正解来了.

    反正是先写了爆搜的 爆搜最近越写越熟练了

    容易想到dp 容易设出状态 f[i][j]表示以i为根的子树内白色的值为j此时黑色的值怎么样。

    可以发现 当白色值固定的时候黑色值可能有多个 所以合法不合法这个状态不太行。

    可以上f[i][j][k]了 不过这样复杂度极高 转移很暴力 不一定能跑过40.

    考虑 对于一个白色颜色和为j来说 那么黑色和 有k1 k2都是合法了 容易得到只有较小的一个才有用。

    那么就有状态了 f[i][j]表示以i为根的子树内白色的值为j此时黑色和的最小值。

    复杂度(nv^2)

    不过对于链的情况 复杂度会进一步达到(nv)可以通过60分。

    容易想到 最后的复杂度应该是nv的。

    观察状态转移式 可以发现 儿子之中只有两种状态有效 一个是 f[tn][v[tn]] 一个是 f[tn][k]=v[tn]。

    前者 只有一个值直接对父亲进行更新即可。后者显然最小k是最优的 也直接对父亲进行贡献即可。

    综上复杂度为nv.

    const int MAXN=1010;
    int T;
    int n,maxx,flag,len;
    int f[MAXN][5003];//f[i][j]表示以i为根的子树内当白色的和为j时黑色的最小值.
    int lin[MAXN],ver[MAXN],v[MAXN],nex[MAXN],g[5003];
    inline void add(int x,int y)
    {
    	ver[++len]=y;
    	nex[len]=lin[x];
    	lin[x]=len;
    }
    inline void dp(int x)
    {
    	int mark=0;
    	go(x)
    	{
    		dp(tn);
    		if(!mark)rep(0,maxx,j)f[x][j]=f[tn][j];
    		else 
    		{
    			int ww=INF;
    			rep(0,maxx,j)
    			{
    				g[j]=f[x][j],f[x][j]=INF;
    				if(f[tn][j]==v[tn]&&ww==INF)ww=j;
    			}
    			//rep(0,maxx,j)rep(0,j,k)f[x][j]=min(f[x][j],g[j-k]+f[tn][k]);
    			//容易想到对于某个儿子 如果其选择白色 显然只有f[v[x]]的状态有效.
    			//如果选择黑色 存在只有f[tn][j]=v[x]的状态有效.
    			//可以实现转移降低复杂度. 前者直接转移 后者找到最小的进行转移 可以证明是最优的.
    			if(f[tn][v[tn]]<INF)
    			{
    				rep(0,maxx,j)if(j+v[tn]<=maxx)f[x][j+v[tn]]=min(f[x][j+v[tn]],f[tn][v[tn]]+g[j]);
    				else break;
    			}
    			if(ww!=INF)
    			{
    				rep(0,maxx,j)if(j+ww<=maxx)f[x][j+ww]=min(f[x][j+ww],g[j]+f[tn][ww]);
    				else break;
    			}
    		}
    		mark=1;
    	}
    	if(!mark)f[x][v[x]]=0,f[x][0]=v[x];
    	else
    	{
    		rep(0,maxx,i)g[i]=f[x][i],f[x][i]=INF;
    		rep(0,maxx,i)
    		{
    			//当前选择白色
    			if(i<=v[x])f[x][v[x]]=min(f[x][v[x]],g[i]);
    			//当前选择黑色
    			f[x][i]=min(f[x][i],(g[i]<=v[x]?v[x]:INF));
    		}
    	}
    }
    int main()
    {
    	freopen("coloring.in","r",stdin);
    	freopen("coloring.out","w",stdout);
    	get(T);
    	while(T--)
    	{
    		get(n);len=0;maxx=5000;flag=0;
    		rep(1,n,i)
    		{
    			lin[i]=0;
    			memset(f[i],0x3f,sizeof(f[i]));
    		}
    		rep(2,n,i)add(read(),i);
    		rep(1,n,i)get(v[i]);
    		dp(1);
    		rep(0,maxx,i)if(f[1][i]<INF)flag=1;
    		if(flag)puts("POSSIBLE");
    		else puts("IMPOSSIBLE");
    	}
    	return 0;
    }
    
  • 相关阅读:
    css--display详解
    关于overflow:hidden的作用
    float 应用
    flex 布局
    CSS+DIV布局中absolute和relative的区别
    线程和进程的区别是什么?
    .NET中读写SQL Server数据库需要用到哪些类?作用是什么?
    C# CookieHelper
    git pull/push代码 每次都要输入账户名和密码的解决方法
    git拉取远程分支到本地
  • 原文地址:https://www.cnblogs.com/chdy/p/13031406.html
Copyright © 2011-2022 走看看