https://codeforces.com/gym/103389/problem/C
题意:一共有 n n n 个景点,每个景点属于一个公司,并给出每个公司的价值,如果是第一次到这个公司就会获得该公司的价值,问从1到每一个景点分别能获得的最大价值。有 m m m 个缆车,连接着两个景点,且景点只能从小往大走
思路:用 p a t h path path 数组来存每个点的缆车接下来可以到达的点,由于最多每次都可能有36种选择,太大数组无法开,所以考虑状态压缩,用二进制来存可以去的点,只需要 2 36 2^{36} 236 就可以存。如 p a t h 1 = 10100 = 20 path_1 = 10100 = 20 path1=10100=20 表示可以 1 1 1 可以去 3 , 5 3, 5 3,5,不可去 2 , 4 2,4 2,4,可以去几从下往上第几位就是 1 1 1
//表示从景点 u 可以到 v
for (int i = 1; i <= m; i++)
{
ll u, v;
cin >> u >> v;
path[u] = path[u] | ((ll)1 << v);
//若path[u]到 v 的那一点本身就为1,即有重复缆车,只需记录一条
}
考虑到可能有价值低的走法,如 1 − 1 - 1−> 2 2 2, 2 − 2 - 2−> 3 3 3, 1 − 1 - 1−> 3 3 3 同时存在,但 1 − 1 - 1−> 2 2 2, 2 − 2 - 2−> 3 3 3 显然比 1 − 1 - 1−> 3 3 3 更优,所以利用 F l o y d Floyd Floyd 进行优化
//若 i 可以到 j 且 i 可以到 k , k 可以到 j ,那么删去 i 到 j 的边
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= j; k++)
if (path[i] & ((ll)1 << k) && path[k] & ((ll)1 << j) && path[i] & ((ll)1 << j))
path[i] -= (ll)1 << j;
d
p
i
dp_i
dpi 表示当前走法走到
i
i
i 能得到的最大价值,
a
n
s
i
ans_i
ansi 记录答案,
c
i
c_i
ci 记录点
i
i
i 的公司,
w
i
w_i
wi 记录第
i
i
i 个公司的价值,注意
w
i
w_i
wi 不是第
i
i
i 个景点的价值,
f
a
fa
fa表示到当前点的上一起点
接下来开始从初始节点 1 往下深搜遍历所有点。用
v
i
s
vis
vis 数组记录是否去过某个公司。
v
i
s
i
=
=
0
vis_i==0
visi==0,那么现在的点 now 得到的价值肯定会加上当前公司的价值,即
d
p
n
o
w
=
d
p
f
a
+
w
c
n
o
w
;
v
i
s
i
=
=
1
dp_{now} = dp_{fa} +w_{c_{now}}\ ;\ vis_i==1
dpnow=dpfa+wcnow ; visi==1,那么
d
p
n
o
w
=
d
p
f
a
dp_{now} = dp_{fa}
dpnow=dpfa,再从该点往下遍历更后面的点
void dfs(ll now, ll fa)
{
if (!vis[c[now]])
dp[now] = dp[fa] + w[c[now]];
else
dp[now] = dp[fa];
ans[now] = max(ans[now], dp[now]);
//注意用vis[c[now]]++ 和 vis[c[now]]--
//若写为vis[c[now]] = 1 和 vis[c[now]] = 0 ,在所有景点都是同一个公司的时候 if (!vis[c[now]]) 的判断会一直为真
vis[c[now]]++;
for (int i = 1; i <= n; i++)
{
if (path[now] & ((ll)1 << i))
{
dfs(i, now);
vis[c[i]]--;
}
}
}
代码:
ll n, m;
ll c[50], w[50], vis[50];
ll dp[50], path[50], ans[50];
void dfs(ll now, ll fa)
{
if (!vis[c[now]])
dp[now] = dp[fa] + w[c[now]];
else
dp[now] = dp[fa];
ans[now] = max(ans[now], dp[now]);
vis[c[now]]++;
for (int i = 1; i <= n; i++)
{
if (path[now] & ((ll)1 << i))
{
dfs(i, now);
vis[c[i]]--;
}
}
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> c[i];
for (int i = 1; i <= n; i++)
cin >> w[i];
for (int i = 1; i <= m; i++)
{
ll u, v;
cin >> u >> v;
path[u] = path[u] | ((ll)1 << v);
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= j; k++)
if (path[i] & ((ll)1 << k) && path[k] & ((ll)1 << j) && path[i] & ((ll)1 << j))
path[i] -= (ll)1 << j;
dfs(1,0);
for (int i = 1; i <= n; i++)
cout << ans[i] << endl;
}
使用深度优先搜索解决景点游览最大价值问题
1031

被折叠的 条评论
为什么被折叠?



