博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UVA - 10859 Placing Lampposts 放置街灯
阅读量:5992 次
发布时间:2019-06-20

本文共 2181 字,大约阅读时间需要 7 分钟。

Placing Lampposts

传送门:

题目大意:给你一片森林,要求你在一些节点上放上灯,一个点放灯能照亮与之相连的所有的边。问你最小化防止的灯数,在灯数相同的条件下,最大化两个点都有灯的边数。

题解:

  首先有一个套路,也是做了此题才知道的,很神奇啊。最小化灯的数量,我们设灯数为V1,把“最大化两个点都有灯的边数”转化为“最下化只有一个点有灯的边数”,设为V2,那么我们设Val=Eps*V1+V2。这样只要DP一个值就可以了。Eps设成一个足够大的值,保证Eps>sum{V2}。此题姑且设为2000。

  然后我们就可以DP了。树上求解最优解,此题为森林,转化为每棵树的答案相加就可以了。那么怎么DP呢?
  设状态DP[i]代表i节点与它的子树以及连向父亲的那一条边的最小的Val。每一个节点有放灯与不放灯两种状态,但是我们发现,父亲放不放灯会影响儿子放不放灯,那么我们再加上一维的状态:dp[i][0/1]代表代表i节点与它的子树以及连向父亲的那一条边的最小的Val,j=1为父亲放灯,j=0代表父亲不放灯。
考虑两种方案:
1.  i放灯:i放灯的话,对于其他的没有什么要求,所以dp[i][j]+=dp[son][1],dp[i][j]+=Eps。如果当前j==0,并且不是根节点,那么dp[i][j]++,因为到父亲的那一条边只有1个灯。
2.  i不放灯:i不放灯,转移就有限制条件了,必须父亲放灯,或者i为根节点,dp[i][1]+=dp[son][0],如果i不是根节点,那么还要++,同样的因为到父亲的那一条边只有1个灯。
  然后一边dfs一边DP就可以了。注意状态转移是错综复杂的,并不是单一的0->1或0->0,具体顺序见代码。
  条件1可以更新j=1和0的情况;条件2只能更新j=1的情况,但是在根节点也可以更新j=0的情况。

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #define RG register 8 #define LL long long 9 #define fre(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);10 using namespace std;11 const int MAXN=5000,Eps=2000;12 int n,num,m,Case,ans;13 int dp[MAXN][2];14 int head[MAXN],to[MAXN],Next[MAXN];15 bool vis[MAXN];16 void dfs(int u,int fa)17 {18 vis[u]=1;19 int sum1=0,sum2=Eps;20 for(int i=head[u];i;i=Next[i])21 {22 int v=to[i];23 if(v==fa)continue;24 dfs(v,u);25 sum1+=dp[v][0];//不放灯26 sum2+=dp[v][1];//放灯27 }28 if(fa!=0)sum1++;29 dp[u][1]=sum1;30 dp[u][1]=min(dp[u][1],sum2);//与放灯的再比较一下。31 dp[u][0]=sum2;32 if(fa!=0) dp[u][0]++;33 if(fa==0)34 dp[u][0]=min(dp[u][0],sum1);35 }36 void add(int f,int t)37 {38 Next[++num]=head[f];39 to[num]=t;40 head[f]=num;41 }42 int main()43 {44 scanf("%d",&Case);45 while(Case--)46 {47 scanf("%d%d",&n,&m);48 num=0;49 memset(head,0,sizeof head);50 memset(vis,0,sizeof vis);51 memset(dp,0,sizeof dp);52 for(int i=1,a,b;i<=m;i++)53 {54 scanf("%d%d",&a,&b);55 a++,b++;56 add(a,b); add(b,a);57 }58 ans=0;59 for(int i=1;i<=n;i++)60 if(!vis[i])61 {62 dfs(i,0);63 ans+=min(dp[i][0],dp[i][1]);64 }65 printf("%d %d %d\n",ans/Eps,m-ans%Eps,ans%Eps);66 }67 return 0;68 }

 

转载于:https://www.cnblogs.com/D-O-Time/p/7663692.html

你可能感兴趣的文章
C语言Makefile文件使用
查看>>
C#编程(十三)----------方法重载
查看>>
GentleNet使用之详细图解[语法使用增强版]
查看>>
php进程的SIGBUS故障
查看>>
telnet测试制定地址端口号
查看>>
保持Service不被Kill掉的方法--双Service守护 && Android实现双进程守护
查看>>
android 截取指定位置字符串
查看>>
李洪强iOS开发之initWithFrame,initWithCoder和aweakFormNib
查看>>
Android ActivityManager.killBackgroundProcesses方法去结束
查看>>
数据库设计原则(转载)
查看>>
MySQL 触发器简单实例
查看>>
Elasticsearch基本概念及核心配置文件详解
查看>>
一次使用Python连接数据库生成二维码并安装为windows服务的工作任务
查看>>
ios_基础篇1_关键字(strong和weak)
查看>>
PageControl
查看>>
我的友情链接
查看>>
远程桌面用户输入法的配置
查看>>
【Getty】Java NIO框架设计与实现
查看>>
常用监控命令工具-----vmstat
查看>>
iCloud存储原理与部分操作
查看>>