荒诞

【问题描述】
我的灵魂与我之间的距离如此遥远,而我的存在却如此真实。
——加缪《局外人》
我醒来的时候,发现满天星斗照在我的脸上。田野上的声音一直传到我的耳
畔。夜的气味,土地的气味,海盐的气味,使我的两鬓感到清凉。这沉睡的夏夜
的奇妙安静,像潮水一般浸透我的全身。这时,长夜将尽,汽笛叫了起来。它宣
告有些人踏上旅途,要去一个从此和我无关痛痒的世界。
这时我在想一个问题:我有一个 n 个点,m 条边的无向图,第 i 个点建立一
个旅游站点的费用是 c_i。特别地,这张图中的任意两点间不存在节点数超过 10
的简单路径。
为了把一切都做得完善,为了使我感到不那么孤独,我想要建造一些旅游站
点使得每个点要么建立了旅游站点,要么与它有边直接相连的点里至少有一个点
建立了旅游站点。我还希望这个建造方案总花费尽量少。
请求出这个花费。
【输入格式】
文件第一行有两个正整数 n 和 m。
第二行包含 n 个整数,其中第 i 个数为 c_i,表示在第 i 个点建立旅游站点
的费用。
接下来 m 行,每行两个正整数 u,v,表示一条边(u,v),保证没有重边。
【输出格式】
输出只有一行,表示最小的总花费。
【样例输入输出】
absurdity.in
6 6
3 8 5 6 2 2
1 2
2 3
1 3
3 4
4 5
4 6
absurdity.out
7
【数据范围】
对于前 10%的测试点,满足所有的 c_i 相等。
对于前 30%的测试点,满足 1<=n<=20,0<=m<=50。
对于另外 15%的测试点,满足每个连通块都是一棵树。
对于 100%的测试点,满足 1<=n<=2*10^4,0<=m<=2.5*10^4,0<=c_i<=10^4。

很鬼的一道题

两点之间路径点数不超过10,意味着联通块的DFS树深度<=10

现在进行树形dp的唯一难点在于一个点会与祖先相互影响,于是考虑状压

f[i][S]表示第i层(x号点)祖先状态为S的最小方案

S是一个3进制数,0表示未选未覆盖,1表示已选,2表示未选被覆盖

我们枚举x连向祖先的边,如果一个祖先状态为1,说明x可以不选

f[i][S+2*pw[i-1]]=min(f[i-1][S])

我们设S'为选x的状态,S'=S+pw[i-1]

如果一个祖先状态为0,那么把它填成2:S'=S'+2*pw[j-1]

于是我们又得到了选x的状态

f[i][S']=min(f[i-1][S]+cost[x])

因为还要考虑子孙对x的影响,所以还要在递归子树后转移

f[i][S]=min(f[i+1][S+pw[i]],f[i+1][S+2*pw[i]])

即子节点被控制

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 struct Node
 7 {
 8   int next,to;
 9 }edge[50001];
10 int c[10001];
11 int head[10001],num,n,fa[10001],dep[10001],f[2][60000],INF,pw[15],m,ans;
12 bool vis[10001];
13 void add(int u,int v)
14 {
15   num++;
16   edge[num].next=head[u];
17   head[u]=num;
18   edge[num].to=v;
19 }
20 void dfs(int x)
21 {int i;
22   vis[x]=1;
23   for (i=head[x];i;i=edge[i].next)
24     {
25       int v=edge[i].to;
26       if (vis[v]==0)
27     {
28       fa[v]=x;
29       dep[v]=dep[x]+1;
30       dfs(v);
31     }
32     }
33 }
34 void dp(int x)
35 {int i,j,s;
36   bool link[11];
37   int cur=dep[x]&1;
38   memset(f[cur],127/2,sizeof(f[cur]));
39   INF=f[cur][0];
40   memset(link,0,sizeof(link));
41   for (i=head[x];i;i=edge[i].next)
42     {
43       int v=edge[i].to;
44       if (dep[v]<dep[x]) link[dep[v]-1]=1;
45     }
46   for (s=0;s<pw[dep[x]-1];s++)
47     {
48       if (f[cur^1][s]>=INF) continue;
49       int ss=s+pw[dep[x]-1];
50       bool flag=0;
51       for (i=0;i<dep[x]-1;i++)
52     if (link[i])
53     {
54       if (!(ss/pw[i]%3)) ss+=pw[i]*2;
55       if ((s/pw[i]%3)==1) flag=1;
56     }
57       f[cur][ss]=min(f[cur][ss],f[cur^1][s]+c[x]);
58       f[cur][s+flag*2*pw[dep[x]-1]]=min(f[cur][s+flag*2*pw[dep[x]-1]],f[cur^1][s]);
59     }
60   for (i=head[x];i;i=edge[i].next)
61     {
62       int v=edge[i].to;
63       if (fa[v]==x)
64     {
65       dp(v);
66       for (s=0;s<pw[dep[x]];s++)
67         f[cur][s]=min(f[cur^1][s+pw[dep[x]]],f[cur^1][s+2*pw[dep[x]]]);
68     }
69     }
70 }
71 int main()
72 {int i,u,v;
73   cin>>n>>m;
74   pw[0]=1;
75   for (i=1;i<=10;i++)
76     pw[i]=pw[i-1]*3;
77   for (i=1;i<=n;i++)
78     {
79       scanf("%d",&c[i]);
80     }
81   for (i=1;i<=m;i++)
82     {
83       scanf("%d%d",&u,&v);
84       add(u,v);add(v,u);
85     }
86   for (i=1;i<=n;i++)
87     if (vis[i]==0)
88     {
89       dep[i]=1;
90       dfs(i);
91       f[0][0]=0;
92       dp(i);
93       ans+=min(f[1][1],f[1][2]);
94       memset(f,127/2,sizeof(f));
95     }
96   cout<<ans;
97 }

 

posted @ 2017-11-06 19:08  Z-Y-Y-S  阅读(256)  评论(0编辑  收藏  举报