【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 }
posted @ 2012-06-02 21:06  datam  阅读(614)  评论(0编辑  收藏  举报