UVA10859 放置街灯 Placing Lampposts(树状DP)



这道题有两种解决方法,因为原图保证无重边无环无自环, 所以原图一定是一颗树(或森林).,都是树状DP,但是实现的过程大同小异,先说第一种吧:
一:

1.在这道题中有两个因素在影响着转移,一个是放置灯的数量,一个是如果放的灯的数量相同的情况下被两盏灯同时照亮的边的条数,但是这两个条件有个优先级,就是灯的个数大于被边同时被两盏灯照亮的边,那么我们是不是可以这样想,如果多了一个灯变量值+1000,而如果是灯照亮了一条边就+1,那么我们在最后就可以把ans/1000就是灯的个数,把ans%1000就是被照亮一条边的个数,那么被两盏灯照亮的的边的个数就是 m - ans%1000,这样一下就能用一个变量来表示三个值,但是我们要注意,要是被一盏灯照亮的边的个数超过1000了呢,那么这样的答案就是错误的,所以我们要使(1000这个数)大于边的个数,那么就避免了这种情况,在这里M是<1000的,所以我们用1000也是可以的,保险起见我们用两千。
2.值表示完了我们开始想动态数组和动态转移方程:
动态数组:dp[i][j]:编号为i 的节点的父亲节点的状态时j(0是不亮,1是亮),可能很多同学比较迷惑为什么要用父亲节点的状态,刚开始我也是比较迷惑,但是后来在写记忆化搜索的时候理解了,首先我们发现一个点可以有亮和不亮两种状态,亮是一定符合的,但是如果让这个点不亮的情况是否存在就需要判断了,而判断的依据主要就是父亲节点的状态,如果a–b,a如果不亮了,那么b一定是需要亮的,因为如果b不亮在,那么a和b的这条边就没有被点亮,与题目要求不符合,所以这就是我们为什么要用到父亲节点的状态的原因,可能有同学就想根节点怎么办呢?所以我们要再加一个特判,如果是根节点,那么这个点亮和不亮都能继续搜索下去,所以综上我们就可以知道:

int DP(int node,int start,int fa){//node 是当前节点,start是父节点的状态,fa是父节点的编号if(vis[node][start])return dp[node][start];//记忆化搜索,这个点之前搜索过vis[node][start] = 1;int ans = 2000;//因为亮灯是绝对可以的操作for(int i=0;i<map[node].size();i++){int a = map[node][i];if(a != fa)//因为只能继续向他的子节点进行递归搜索,因为如果不加这个判断条件的话就又找回去了,所以我们要避免这种情况 map[node][start] += DP(a,1,node);//下一个节点是a,因为父亲是node,node的状态时亮灯,所以参数是1;}/==在这里求完了之后我们要注意一点就是我们不要忘记了边被一盏灯照亮的情况也是需要综合考虑的!!!==if(fa != -1 && start == 0) ans++;/在这里我们把灯亮的情况找完了,然后我们就要判断这盏灯能不能不亮,而灯可以不亮的情况只有两种,一种是这个点是根节点,一种是父亲节点的灯是亮着的if(fa==-1 || start){int sum = 0;for(int i=0;i<map[node].size();i++){int a = map[node][i];if(a != fa){ /上面我们已经解释过了sum += DP(a,0,node);}}if(start) sum++; /同样要考虑被一盏灯照亮的边ans = min(ans,sum); /取最优}/这样我们就求出最优的ans了,因为ans的值是dp[node][start]的值,所以不要忘了dp[node][start] = ans;dp[node][start] = ans;return ans;
}

每次从根节点做完一次DP之后以该点作为根节点的树就全部遍历完了,但是有可能是一个森林,所以我们要进行遍历:

for(int i=0;i<n;i++){if(vis[i][0]==0){ans += DP(i,0,-1);//该点作为根节点进行遍历,ans加上这棵树的值}
}

输出结果就好了

 printf("%d %d %d\n",ans1/k,m-ans1%k,ans1%k);//k也就是我们前面提到的2000

完整代码如下:

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 5000 + 10;
vector<int>map[maxn];
int dp[maxn][2];
int vis[maxn][2];
int k = 2000;
int DP(int node,int start,int fa){  if(vis[node][start]) return dp[node][start];vis[node][start] = 1;int ans = k;for(int i=0;i<map[node].size();i++){if(map[node][i]!=fa) ans += DP(map[node][i],1,node);}if(!start && fa>=0) ans++;if(start || fa<0){int sum = 0;for(int i=0;i<map[node].size();i++){if(map[node][i]!=fa) sum += DP(map[node][i],0,node);}if(fa >= 0) sum++;ans = min(ans,sum);}dp[node][start] = ans;return ans;
}
int main(){int t;cin >> t;while(t--){int n,m;cin >> n >> m;for(int i=0;i<=n;i++) map[i].clear();memset(vis,0,sizeof(vis));
//      memset(dp,0,sizeof(dp));for(int i=0;i<m;i++){int a,b;cin >> a >> b;map[a].push_back(b);map[b].push_back(a);}int ans1 = 0;for(int i=0;i<n;i++){if(!vis[i][0]){ans1 += DP(i,0,-1);}}printf("%d %d %d\n",ans1/k,m-ans1%k,ans1%k);}return 0;
}

第二种做法:
很多同学觉得不太舒服,那么我们就把dp[i][j]作为这个点的亮还是不亮的信息,同理,亮是1,不亮是0;
我们在这里看代码进行分析:

void dfs(int node,int fa){ /该点的编号与该点父亲的编号,如果父亲是-1,那么这个点就是根节点vis[node] = 1; /这个点被访问过了,在后面的统计中说明这个点所在的树已经被统计过了dp[node][0] = 0; /这个点不亮dp[node][1] = k; /这个点亮,亮的话就要+2000,也就是+kfor(int i=0;i<map[node].size();i++){if(map[node][i]!=fa){//不是父节点,第一种情况已经给出说明dfs(map[node][i],node);//继续往下进行搜索int a = map[node][i];dp[node][0] += dp[a][1] + 1; /z这个点不亮然而他的儿子节点亮,所以这个点是被一盏灯照亮的,所以把加上儿子节点亮的值再加一dp[node][1] += min(dp[a][0]+1,dp[a][1]); /这个点是亮的,所以他的儿子可以亮可以不亮,取最小的就好,如果他的儿子不亮,那么说明他与他儿子连接的这条边是只被一盏灯照亮的,所以+1,}}
}
for(int i=0;i<n;i++){if(!vis[i]){//如果该点没有被访问过,说明这个点所在的树没有被访问过,那么就以这个点作为根进行dfsdfs(i,-1);ans += min(dp[i][0],dp[i][1]);//取这种情况下最小的那个}
}

完整代码如下:

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 1000+10;
vector<int>map[maxn];
int dp[maxn][2];
int vis[maxn];
int k = 2000;
void dfs(int node,int fa){vis[node] = 1;dp[node][0] = 0;dp[node][1] = k;for(int i=0;i<map[node].size();i++){if(map[node][i]!=fa){dfs(map[node][i],node);int a = map[node][i];dp[node][0] += dp[a][1] + 1;dp[node][1] += min(dp[a][0]+1,dp[a][1]);}}
}
int main(){int t;cin >> t;while(t--){int n,m;cin >> n >> m;for(int i=0;i<n;i++) map[i].clear();memset(dp,0,sizeof(dp));memset(vis,0,sizeof(vis));for(int i=0;i<m;i++){int a,b;cin >> a >> b;map[a].push_back(b);map[b].push_back(a);}int ans = 0;for(int i=0;i<n;i++){if(!vis[i]){dfs(i,-1);ans += min(dp[i][0],dp[i][1]);}}printf("%d %d %d\n",ans/k,m-ans%k,ans%k);}return 0;
}

UVA10859 放置街灯 Placing Lampposts(树状DP)相关推荐

  1. 10_放置街灯(Placing Lampposts,UVa 10859)

    问题来源:刘汝佳<算法竞赛入门经典--训练指南> P70 例题30: 问题描述:有给你一个n个点m条边(m<n<=1000)的无向无环图,在尽量少的节点上放灯,使得所有边都被照 ...

  2. uva 10859 放置街灯--Placing Lampposts

    uva 10859 - Placing Lampposts(树形dp ###两个别人家的代码,没有注释看了很久 ###所以自己改写了一遍,附注释 https://blog.csdn.net/keshu ...

  3. hdu 1561 The more, The Better_树状dp

    题目链接 题意:给你一棵树,各个节点都有价值(除根节点),从根节点出发,选择m个节点,问最多的价值是多小. 思路:很明显是树状dp,遍历树时背包最优价值,dp[i][k]=max{dp[i][r]+d ...

  4. poj 1655 Balancing Act 树状dp

    //poj1655树状dp //求一个数的重心 // dfs找到节点v的各个子树的节点数 // 取最大值设为num[v],而v节点以上的子树的节点数 // 为总数-num[v]-1(包括v节点本身)设 ...

  5. HDU 4123 树状DP+RMQ

    /*************************************************************************************************** ...

  6. uva10859放置街灯(树形dp)

    例题 30 放置街灯( Placing Lampposts, UVa 10859 ) 给你一个 n 个点 m 条边的无向无环图, 在尽量少的结点上放灯, 使得 所有边都被照亮. 每盏灯将照亮以它为一个 ...

  7. 树状dp(这个人写得好多转来慢慢看)

    树状动态规划定义 之所以这样命名树规,是因为树形DP的这一特殊性:没有环,dfs是不会重复,而且具有明显而又严格的层数关系.利用这一特性,我们可以很清晰地根据题目写出一个在树(型结构)上的记忆化搜索的 ...

  8. UVA 1220 Party at Hali-Bula (树状DP+记忆化搜索)

    Dear Contestant, I'm going to have a party at my villa at Hali-Bula to celebrate my retirement from ...

  9. 【tyvj1052】【树状dp】没有上司的舞会

    描述 Ural大学有N个职员,编号为1~N.他们有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.每个职员有一个快乐指数.现在有个周年庆宴会,要求与会职员的快乐指数最大 ...

最新文章

  1. C++ STL lower_bound,upper_bound的使用总结
  2. vue native
  3. 关于Java序列化你应该知道的一切
  4. Python 揭秘斐波那契定律,如何帮助码农分析股票?| 技术头条
  5. Android 换肤功能的实现(Apk插件方式)
  6. 10.04更新源问题
  7. Java连接数据库的步骤
  8. iOS-调用系统的短信和发送邮件功能,实现短信分享邮件分享
  9. oppoA83怎么升级android版本,OPPOA83系统刷机包(官方最新固件升级包正式版-A.37)...
  10. css三角形的IE兼容写法
  11. 超市微信小程序怎么做_微信小程序便利店怎么开?便利店和百货超市怎么开发小程序?...
  12. Singularity 快速上手, 带你飞
  13. opencv------绘制文本
  14. 国内最新餐饮品牌全案设计十大排名(2023年榜单)
  15. 谷歌colab“几键”运行图像超分辨率模型-ESRGAN,操作详解
  16. 中国信通院:2017年Q3共享单车行业发展指数报告(附下载)
  17. 计算机未响应硬盘,最近电脑打开磁盘或文件夹老程序未响应为什么啊,有什么办法可以解决?...
  18. Java实现excel大数据量导入
  19. 修改传奇私服服务器,传奇SF 肿么修改服务器时间
  20. 成为测试/开发程序员,小张:现实就来了个下马威......

热门文章

  1. mysql如何查看事务日记_详解 Mysql 事务和Mysql 日志
  2. html5源码笔记(三)【爱创课堂专业前端培训】
  3. 网络安全课程笔记(1)
  4. ggplot2设置坐标轴范围_使用 ggplot2 进行高级绘图—R读书笔记(二)
  5. MSDN无法打开的解决办法
  6. 学习Android启动初始化 App StartUp
  7. 06_03_SpringMVC 任务一: SpringMVC基本应用(SpringMVC请求与响应,进行日期转换工具类DateConverter)
  8. SQL 2012 拒绝了对对象 '表名‘' (数据库 'xxxxx',架构 'dbo')的 SELECT 权限。
  9. javaFX学习之scene场景的概念
  10. 用递归树方法求解递归式