http://codeforces.com/gym/100502/attachments

题意:有n个点,容量为tol,接下来n个关系,表示选了第i个点,那么第xi个点就必须被选。问最多可以选多少个点使得不超过容量tol。

思路:由题目样例可得,边可能出现自环的情况,这个时候这条边其实没用。然后因为是一个图,所以需要缩点,缩完之后用一个sz数组表示点的大小,重新建一幅图。因为有可能是森林,所以需要添加一个虚根,使得其变成一棵树。然后题目就转变为求有依赖的树上背包了。

我是学了这篇博客的写法:http://blog.csdn.net/y990041769/article/details/38068223

dp[i][j]表示以i为根的子树在容量为j的时候能放的点的最大个数。sz[i]表示i结点的大小。

因为i是必须选的(不然其儿子都没办法选),那么一开始就直接赋予其值。

         for(int j = tol; j >= sz[u]; j--) { // 枚举容量
for(int k = ; k <= j - sz[u]; k++) { // k表示能分配给子节点的容量
dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k]);
}
}

接下来第一层循环是像01背包一样,枚举能够放下u(根结点)自己的容量,第二层循环k代表能够分配给儿子的容量,因为根结点自己已经放进去了,所以是j-sz[u]。就这样可以更新完u这个根节点了。

 #include <bits/stdc++.h>
using namespace std;
#define N 1010
struct Edge {
int v, nxt;
} edge[N*], e[N*];
int n, tol, head[N], tot, h[N], t, dfn[N], low[N], belong[N], vis[N], num, tid, sz[N], deg[N], dp[N][N];
stack<int> sta; void Add(int u, int v) { edge[tot] = (Edge) {v, head[u]}; head[u] = tot++; }
void add(int u, int v) { e[t] = (Edge) {v, h[u]}; h[u] = t++; } void tarjan(int u) {
sta.push(u);
vis[u] = ;
dfn[u] = low[u] = ++tid;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if(vis[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]) {
num++;
int top = -;
while(top != u) {
top = sta.top(); sta.pop();
belong[top] = num;
sz[num]++;
vis[top] = ;
}
}
} void BuildGraph() {
memset(head, -, sizeof(head));
memset(h, -, sizeof(h));
t = tot = ;
for(int i = ; i <= n; i++) {
int j; scanf("%d", &j);
if(i == j) continue;
Add(j, i);
}
for(int i = ; i <= n; i++)
if(!dfn[i]) tarjan(i); // 将环缩点重新建图
for(int u = ; u <= n; u++) {
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(belong[u] != belong[v]) {
add(belong[u], belong[v]);
deg[belong[v]]++;
}
}
}
for(int i = ; i <= num; i++) // 设一个虚根将森林变成树
if(!deg[i]) add(, i);
} void dfs(int u) {
for(int i = tol; i >= sz[u]; i--) dp[u][i] = sz[u]; // 初始状态必须有这个结点的sz
for(int i = h[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
dfs(v);
for(int j = tol; j >= sz[u]; j--) { // 枚举容量
for(int k = ; k <= j - sz[u]; k++) { // k表示能分配给子节点的容量
dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k]);
}
}
}
} void Bag() {
dfs();
printf("%d\n", dp[][tol]);
} int main() {
scanf("%d%d", &n, &tol);
BuildGraph();
Bag();
return ;
}

最新文章

  1. 在win7环境下批量修改文件权限
  2. Visual Studio将std::cout输出到Output窗口
  3. PHP单例模式
  4. JSP Filter用法
  5. LoadRunner测试结果分析02 转载至zhangzhe的新浪博客
  6. android 开发 简单的页面布局
  7. Java基础之创建窗口——使用流布局管理器(TryFlowLayout)
  8. android 自定义按钮实现 home键 和返回键
  9. IOS 学习日志 2015-3-13
  10. JAVA模拟各种请求方式访问RESTFUL
  11. grunt live
  12. Oracle改变字段类型
  13. Errors running builder &#39;Integrated External Tool Builder&#39; on project xxx
  14. [Swift]LeetCode502. IPO(首次公开募股) | Initial Public Offerings
  15. Windows 快捷键总结
  16. redis 在 php 中的应用(Hash篇)
  17. C# 动态添加类、动态添加类型、代码添加类型
  18. Struts2 环境配置
  19. 【HDU1710】树的遍历
  20. sqlserver 针对预处理sql传入参数的处理方式

热门文章

  1. C#中将字符串转换成Md5值的方法
  2. 利用属性中设置、查看DataContext Command等
  3. GlyphRun 对象和 Glyphs 元素简介
  4. WPF 启动唯一程序(项目,exe,实例)
  5. VMNET 工作站
  6. 微信小程序把玩(三十七)location API
  7. Windows完成端口与猪肉佬
  8. Delphi中用MessageBox()API函数做倒计时对话框(使用Hook安装CBTHookCallback,计时器更改文字,SetWindowText API真正修改文字,引用未知函数)good
  9. Linux使用技巧:linux下将命令值赋给shell变量
  10. Windows线程生灭(图文并茂)