【BZOJ1495】[NOI2006]网络收费 暴力+DP
【BZOJ1495】[NOI2006]网络收费
Description
网络已经成为当今世界不可或缺的一部分。每天都有数以亿计的人使用网络进行学习、科研、娱乐等活动。然而,不可忽视的一点就是网络本身有着庞大的运行费用。所以,向使用网络的人进行适当的收费是必须的,也是合理的。MY市NS中学就有着这样一个教育网络。网络中的用户一共有2N个,编号依次为1, 2, 3, …, 2N。这些用户之间是用路由点和网线组成的。用户、路由点与网线共同构成一个满二叉树结构。树中的每一个叶子结点都是一个用户,每一个非叶子结点(灰色)都是一个路由点,而每一条边都是一条网线(见下图,用户结点中的数字为其编号)
MY网络公司的网络收费方式比较奇特,称为“配对收费”。即对于每两个用户i, j (1≤i < j ≤2N ) 进行收费。由于用户可以自行选择两种付费方式A、B中的一种,所以网络公司向学校收取的费用与每一位用户的付费方式有关。该费用等于每两位不同用户配对产生费用之和。 为了描述方便,首先定义这棵网络树上的一些概念: 祖先:根结点没有祖先,非根结点的祖先包括它的父亲以及它的父亲的祖先; 管辖叶结点:叶结点本身不管辖任何叶结点,非叶结点管辖它的左儿子所管辖的叶结点与它的右儿子所管辖的叶结点; 距离:在树上连接两个点之间的用边最少的路径所含的边数。 对于任两个用户i, j (1≤i)
由于最终所付费用与付费方式有关,所以NS中学的用户希望能够自行改变自己的付费方式以减少总付费。然而,由于网络公司已经将每个用户注册时所选择的付费方式记录在案,所以对于用户i,如果他/她想改变付费方式(由A改为B或由B改为A),就必须支付Ci元给网络公司以修改档案(修改付费方式记录)。 现在的问题是,给定每个用户注册时所选择的付费方式以及Ci,试求这些用户应该如何选择自己的付费方式以使得NS中学支付给网络公司的总费用最少(更改付费方式费用+配对收费的费用)。
Input
输入文件中第一行有一个正整数N。 第二行有2N个整数,依次表示1号,2号,…,2N号用户注册时的付费方式,每一个数字若为0,则表示对应用户的初始付费方式为A,否则该数字为1,表示付费方式为B。 第三行有2N个整数,表示每一个用户修改付费方式需要支付的费用,依次为C1, C2, …,CM 。( M=2N ) 以下2N-1行描述给定的两两用户之间的流量表F,总第(i + 3)行第j列的整数为Fi, j+i 。(1≤i<2N,1≤j≤2N ? i) 所有变量的含义可以参见题目描述。N≤10,0≤Fi, j≤500,0≤Ci≤500 000
Output
你的程序只需要向输出文件输出一个整数,表示NS中学支付给网络公司的最小总费用。(单位:元)
Sample Input
2
1 0 1 0
2 2 10 9
10 1 2
2 1
3
1 0 1 0
2 2 10 9
10 1 2
2 1
3
Sample Output
8
HINT
【样例说明】 将1号用户的付费方式由B改为A,NS中学支付给网络公司的费用达到最小
题解:先观察表格,发现当nA<nB时,我们可以将选A看成系数为1,选B看成系数为0,这样就可以将点对的贡献拆开来考虑。为此我们预处理cost[i][j],表示i与所有和i的lca深度为j的点构成的点对费用之和,这样我们就相当于将花费都放到了叶子节点处。
但是我们需要知道每个点是nA<nB还是nA>=nB才能确定叶子节点的费用,但由于是完全二叉树,所以我们可以枚举每个节点是nA<nB还是nA>=nB,然后就能确定叶子节点的取A或B的费用,接着用f[x][y]表示x子树中有y个B,其余都是A的最小费用,那么从将一个非叶子节点的两个儿子的DP值进行背包合并便得到了该点的DP值。由于我们已经钦定了该点是nA<nB或是nA>=nB,所以我们在DP时将不合法的状态去掉即可。
以上做法看起来可能比较暴力,不过实际复杂度呢?$T(n)=4(n-1)+2^{2n}$,所以每一层的复杂度都是$O(2^{2n})$的,一共log层;而搜一次叶子节点消耗的时间是n,每个叶子都要被搜到$O(2^n)$次,一共$2^n$个叶子,所以两个地方的复杂度都是$O(2^{2n}n)$的。
然而正解是把我的暴力改成状压。。。都差不多。
goubiGXZ交我程序还加读入优化拿Rank1。
#include <cstdio> #include <cstring> #include <iostream> #define ls x<<1 #define rs x<<1|1 using namespace std; const int maxn=2100; typedef long long ll; int n; ll ans; int tp[maxn],B[20],s[20]; ll cost[maxn][12],f[maxn][maxn],C[maxn][2]; inline int lca(int a,int b) { int i; for(i=n-1;i>=0;i--) if((a>>i)!=(b>>i)) break; return n-i-1; } inline int rd() { int ret=0; char gc=getchar(); while(gc<'0'||gc>'9') gc=getchar(); while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret; } void dfs(int x,int dep) { if(dep==n) { f[x][0]=C[x-B[n]][0]; f[x][1]=C[x-B[n]][1]; for(int i=0;i<dep;i++) f[x][s[i]^1]+=cost[x-B[n]][i]; return ; } s[dep]=0; memset(f[x],0x3f,sizeof(f[x][0])*(B[n-dep]+1)); dfs(ls,dep+1),dfs(rs,dep+1); for(int i=0;i<=B[n-dep-1];i++) for(int j=0;i+j<=B[n-dep-1];j++) f[x][i+j]=min(f[x][i+j],f[ls][i]+f[rs][j]); s[dep]=1; dfs(ls,dep+1),dfs(rs,dep+1); for(int i=1;i<=B[n-dep-1];i++) for(int j=B[n-dep-1]+1-i;j<=B[n-dep-1];j++) f[x][i+j]=min(f[x][i+j],f[ls][i]+f[rs][j]); } int main() { //freopen("network.in","r",stdin); //freopen("network.out","w",stdout); n=rd(); int i,j,v,a; for(i=0;i<=n;i++) B[i]=1<<i; for(i=0;i<B[n];i++) tp[i]=rd(); for(i=0;i<B[n];i++) C[i][tp[i]]=0,C[i][tp[i]^1]=rd(); for(i=0;i<B[n];i++) for(j=i+1;j<B[n];j++) { a=lca(i,j),v=rd(); cost[i][a]+=v,cost[j][a]+=v; } dfs(1,0); ans=1ll<<60; for(i=0;i<=B[n];i++) ans=min(ans,f[1][i]); printf("%lld",ans); return 0; }
| 欢迎来原网站坐坐! >原文链接<