【NOI2006】 网络收费
题目描述
网络收费 【问题描述】 网络已经成为当今世界不可或缺的一部分。每天都有数以亿计的人使用网络 进行学习、科研、娱乐等活动。然而,不可忽视的一点就是网络本身有着庞大的 运行费用。所以,向使用网络的人进行适当的收费是必须的,也是合理的。 MY 市 NS 中学就有着这样一个教育网络。网络中的用户一共有 2N 个,编号 依次为 1, 2, 3, ..., 2N。这些用户之间是用路由点和网线组成的。用户、路由点与 网线共同构成一个满二叉树结构。树中的每一个叶子结点都是一个用户,每一个 非叶子结点(灰色)都是一个路由点,而每一条边都是一条网线(见下图,用户 结点中的数字为其编号) MY 网络公司的网络收费方式比较奇特,称为“ 配对收费 ” 。即对于每两个 N 用户 i, j (1≤i < j ≤2 ) 进行收费。由于用户可以自行选择两种付费方式 A、B 中的一种,所以网络公司向学校收取的费用与每一位用户的付费方式有关。该费 用等于每两位不同用户配对产生费用之和。 为了描述方便,首先定义这棵网络树上的一些概念: 祖先: 根结点没有祖先, 非根结点的祖先包括它的父亲以及它的父亲的祖先; 管辖叶结点:叶结点本身不管辖任何叶结点,非叶结点管辖它的左儿子所管 辖的叶结点与它的右儿子所管辖的叶结点; 距离:在树上连接两个点之间的用边最少的路径所含的边数。 对于任两个用户 i, j (1≤i<j≤2N ),首先在树上找到与它们距离最近的公共 祖先:路由点 P,然后观察 P 所管辖的叶结点(即用户)中选择付费方式 A 与 B 的人数,分别记为 nA 与 nB,接着按照网络管理条例第 X 章第 Y 条第 Z 款进行 收费(如下表) 由于最终所付费用与付费方式有关,所以 NS 中学的用户希望能够自行改变 自己的付费方式以减少总付费。然而,由于网络公司已经将每个用户注册时所选 择的付费方式记录在案,所以对于用户 i,如果他/她想改变付费方式(由 A 改为 (修改付费方式记录) 。 B 或由 B 改为 A)就必须支付 Ci 元给网络公司以修改档案 , 现在的问题是,给定每个用户注册时所选择的付费方式以及 Ci,试求这些用 户应该如何选择自己的付费方式以使得 NS 中学支付给网络公司的总费用最少 (更改付费方式费用+配对收费的费用) 。 【输入格式】 输入文件中第一行有一个正整数 N。 2 ..., N 号用户注册时的付费方式, 2 第二行有 2N 个整数,依次表示 1 号, 号, 每一个数字若为 0,则表示对应用户的初始付费方式为 A,否则该数字为 1,表 示付费方式为 B。 第三行有 2N 个整数, 表示每一个用户修改付费方式需要支付的费用, 依次为 N C1, C2, ...,CM 。( M=2 ) 以下 2N-1 行描述给定的两两用户之间的流量表 F,总第(i + 3)行第 j 列的整 数为 Fi, j+i 。 (1≤i<2N,1≤j≤2N – i) 所有变量的含义可以参见题目描述。 【输出格式】 你的程序只需要向输出文件输出一个整数,表示 NS 中学支付给网络公司的 最小总费用。 (单位:元) 【输入样例】 2 1010 2 2 10 9 10 1 2 21 3 【输出样例】 8 【样例说明】 将 1 号用户的付费方式由 B 改为 A,NS 中学支付给网络公司的费用达到最 小。 【评分方法】 本题没有部分分,你的程序的输出只有和我们的答案完全一致才能获得满 分,否则不得分。 【数据规模和约定】 40%的数据中 N≤4; 80%的数据中 N≤7; 100%的数据中 N≤10,0≤Fi, j≤500,0≤Ci≤500 000。
解法1:搜索
1 (* 2 *Problem: NOI2006 网络收费 3 *Author : Chen Yang 4 *Time : 2012.5.31 5:00 pm 5 *State : 40分 6 *Memo : 搜索 7 *) 8 program network; 9 const max=100000000; 10 var 11 n,m,i,j,ans:longint; 12 c,s:array[0..4000] of longint; 13 sum:array[0..4000,0..2] of longint; 14 f:array[0..2000,0..2000] of longint; 15 //================ 16 procedure maintain(x,y:longint); 17 begin 18 while x>1 do 19 begin 20 x:=x>>1; inc(sum[x,y]); 21 end; 22 end; 23 //================ 24 function root(x,y:longint):longint; 25 begin 26 while x<>y do 27 begin 28 x:=x>>1; y:=y>>1; 29 end; 30 exit(x); 31 end; 32 //================ 33 function calc:longint; 34 var 35 i,j,k,t:longint; 36 begin 37 t:=0; 38 fillchar(sum,sizeof(sum),0); 39 for i:=1 to m do maintain(i+m-1,s[i]); 40 for i:=1 to m do 41 for j:=i+1 to m do 42 begin 43 k:=root(i+m-1,j+m-1); 44 if s[i]<>s[j] then inc(t,f[i,j]) else 45 begin 46 if sum[k,0]<sum[k,1] then 47 begin 48 if s[i]=0 then inc(t,f[i,j]<<1); 49 end else 50 begin 51 if s[i]=1 then inc(t,f[i,j]<<1); 52 end; 53 end; 54 end; 55 exit(t); 56 end; 57 //================ 58 procedure find(x,y:longint); 59 var 60 t:longint; 61 begin 62 if y>=ans then exit; 63 if x=m+1 then 64 begin 65 t:=y+calc; 66 if t<ans then ans:=t; 67 exit; 68 end; 69 find(x+1,y); 70 s[x]:=s[x] xor 1; 71 find(x+1,y+c[x]); 72 s[x]:=s[x] xor 1; 73 end; 74 //================ 75 begin 76 assign(input,'network.in'); reset(input); 77 assign(output,'network.out'); rewrite(output); 78 read(n); m:=1; 79 for i:=1 to n do m:=m*2; 80 for i:=1 to m do read(s[i]); 81 for i:=1 to m do read(c[i]); 82 for i:=1 to m-1 do 83 for j:=1 to m-i do read(f[i,j+i]); 84 ans:=max; 85 find(1,0); 86 writeln(ans); 87 close(input); close(output); 88 end.
解法2:树形状压DP(朴素)
1 /* 2 *Problem: NOI2006 生日快乐 3 *Author : Chen Yang 4 *Time : 2012.5.31 5:00 pm 5 *State : 80分 6 *Memo : 状压dp 7 */ 8 #include <cstdio> 9 #include <cstdlib> 10 #include <string> 11 #include <cstring> 12 #include <algorithm> 13 using namespace std; 14 const int maxm = 257, inf = 1e9; 15 int n, m = 1, ans = inf; 16 int c[maxm], s[maxm], f[maxm][maxm][maxm], cost[maxm][maxm]; 17 18 void find(int x, int y, int S, int high) 19 { 20 if (f[x][y][S] >= 0) return; 21 if (x >= m) 22 { 23 int t = x-m+1; 24 f[x][y][S] = 0; 25 if (y==s[t]) 26 { 27 f[x][y][S] += c[t]; 28 for (int i=0; i<n; ++i) 29 if (((S>>i) & 1) == s[t]) f[x][y][S] += cost[t][x >> (i+1)]; 30 } else 31 { 32 for (int i=0; i<n; ++i) 33 if (((S>>i) & 1) != s[t]) f[x][y][S] += cost[t][x >> (i+1)]; 34 } 35 return; 36 } 37 int p = y < ((1 << high) - y), S1 = (S << 1) | p; 38 f[x][y][S] = inf; 39 int tot = min(1 << (high-1), y); 40 for (int i=y-tot; i<tot+1; ++i) 41 { 42 find(x<<1, i, S1, high-1); 43 find((x<<1)+1, y-i, S1, high-1); 44 f[x][y][S] = min(f[x][y][S], f[x<<1][i][S1]+f[(x<<1)+1][y-i][S1]); 45 } 46 } 47 48 int main() 49 { 50 freopen("network.in", "r", stdin); 51 freopen("network.out", "w", stdout); 52 scanf("%d",&n); m = 1 << n; 53 for (int i=1; i<m+1; ++i) scanf("%d", s+i); 54 for (int i=1; i<m+1; ++i) scanf("%d", c+i); 55 for (int i=1; i<m+1; ++i) 56 for (int j=1; j<m-i+1; ++j) 57 { 58 int x, i1 = i+m-1, j1 = i+j+m-1; 59 scanf("%d", &x); 60 for (;i1!=j1; i1>>=1, j1>>=1); 61 cost[i][i1] += x; cost[i+j][i1] += x; 62 } 63 memset(f, -1, sizeof f); 64 ans = inf; 65 for (int i=0; i<=m; ++i) 66 { 67 find(1, i, 0, n); 68 ans = min(ans, f[1][i][0]); 69 } 70 printf("%d\n",ans); 71 return 0; 72 }
解法2:树形状压DP(优化)
1 /* 2 *Problem: NOI2006 生日快乐 3 *Author : Chen Yang 4 *Time : 2012.5.31 5:00 pm 5 *State : Solved 6 *Memo : 状压dp、维数优化 7 */ 8 #include <cstdio> 9 #include <cstdlib> 10 #include <string> 11 #include <cstring> 12 #include <algorithm> 13 using namespace std; 14 const int maxm = 2049, maxk=5000, inf = 1e9; 15 int n, m = 1, ans = inf; 16 int c[maxm], s[maxm], f[maxm][maxk], cost[maxm][maxm]; 17 18 void find(int x, int y, int S, int high) 19 { 20 int k = (S << (high+1)) | y; 21 if (f[x][k] >= 0) return; 22 if (x >= m) 23 { 24 int t = x-m+1; 25 f[x][k] = 0; 26 if (y==s[t]) 27 { 28 f[x][k] += c[t]; 29 for (int i=0; i<n; ++i) 30 if (((S>>i) & 1) == s[t]) f[x][k] += cost[t][x >> (i+1)]; 31 } else 32 { 33 for (int i=0; i<n; ++i) 34 if (((S>>i) & 1) != s[t]) f[x][k] += cost[t][x >> (i+1)]; 35 } 36 return; 37 } 38 int p = y < ((1 << high)-y), S1 = (S << 1) | p; 39 f[x][k] = inf; 40 int tot = min(1 << (high-1), y); 41 for (int i=y-tot; i<tot+1; ++i) 42 { 43 find(x<<1, i, S1, high-1); 44 find((x<<1)+1, y-i, S1, high-1); 45 int k1 = (S1 << high) | i, k2 = (S1 << high) | (y-i); 46 f[x][k] = min(f[x][k], f[x<<1][k1]+f[(x<<1)+1][k2]); 47 } 48 } 49 50 int main() 51 { 52 freopen("network.in", "r", stdin); 53 freopen("network.out", "w", stdout); 54 scanf("%d",&n); m = 1 << n; 55 for (int i=1; i<m+1; ++i) scanf("%d", s+i); 56 for (int i=1; i<m+1; ++i) scanf("%d", c+i); 57 for (int i=1; i<m+1; ++i) 58 for (int j=1; j<m-i+1; ++j) 59 { 60 int x, i1 = i+m-1, j1 = i+j+m-1; 61 scanf("%d", &x); 62 for (;i1!=j1; i1>>=1, j1>>=1); 63 cost[i][i1] += x; cost[i+j][i1] += x; 64 } 65 memset(f, -1, sizeof f); 66 ans = inf; 67 for (int i=0; i<=m; ++i) 68 { 69 find(1, i, 0, n); 70 ans = min(ans, f[1][i]); 71 } 72 printf("%d\n",ans); 73 return 0; 74 }