[UVA 10859] Placing Lampposts

图片加载可能有点慢,请跳过题面先看题解,谢谢
1196604-20171012170845340-914713091.png
1196604-20171012170850777-234237229.png
1196604-20171012170854152-967577591.png

这道题需要求两样东西:放的灯数 \(a\) 最少,并且被两盏灯照亮的边数 \(b\) 尽量大,一个大一个小贼不爽了,我们改一下
求:放的灯数 \(a\) 最少,并且被一盏灯照亮的边数 \(c\) 尽量小,是不是看着舒服多了
$
$
那这个怎么求呢?并不会
$
$
这儿上一个套路,我们求这样一个东西 \(x=k*a+c\)\(k\) 为一个较大的常数(这道题我取2333),下面给出刘汝佳的说法:
\(k\) 是一个比“ \(c\) 的最大理论值和 \(c\) 的最小理论值之差”还要大的数,这样,只要两个解的 \(a\) 不同,不管 \(c\) 相差多少,\(x\) 都会取决于 \(a\) 的大小,
只有当 \(a\) 相等时才取决于 \(c\)
$
$
那么我们设状态:\(f[i][0/1]\) 表示,\(i\) 的父亲是否放灯时(1放灯,0不放),以 \(i\) 为根的子树的最小 \(x\) 值。
那么有转移:

  1. \(i\) 不放灯,只有当 \(i\) 的父亲放灯或者 \(i\) 为根时才能有此决策,\(f[i][j]=\sum{(v为 i 的儿子)}f[v][0]\) 。如果 \(i\) 不为根,则 \(f[i][j]++\)\(i\) 的父边只有一盏灯照亮);
  2. \(i\) 放灯,\((f[i][j]=\sum{(v 为 i 的儿子)}f[v][1])+k\)。如果 \(j=0\)\(i\) 不是根,则 \(f[i][j]++\)\(i\) 的父边只有一盏灯照亮);
    $
    $
//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define il inline
#define RG register
using namespace std;
il int gi(){ RG int x=0,q=1; RG char ch=getchar(); while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
  if( ch=='-' ) q=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; }

int T,n,m,f[1010][2];
int num,head[1010],nxt[2010],to[2010];
il void add(int u,int v){
   nxt[++num]=head[u];to[num]=v;head[u]=num;
   nxt[++num]=head[v];to[num]=u;head[v]=num;
}

il void init(){
   n=gi(),m=gi(),num=0;
   memset(head,0,sizeof(head));
   for(int i=1;i<=m;i++){ RG int u=gi(),v=gi(); add(u,v); }
}

bool vis[1010][2];
il int dfs(int x,int k,int fa){
   if(vis[x][k]) return f[x][k];
   vis[x][k]=1;
   RG int ret=2333+(!k&&fa>=0);
   for(int i=head[x];i;i=nxt[i]){
      RG int v=to[i]; 
      if(v==fa) continue;
      ret+=dfs(v,1,x);
   }

   if(k||fa<0){
      RG int sum=(fa>=0);
      for(int i=head[x];i;i=nxt[i]){
         RG int v=to[i]; 
         if(v==fa) continue;
         sum+=dfs(v,0,x);
      }
      ret=min(ret,sum);
   }
   return f[x][k]=ret;
}

il void work(){
   int ans=0;
   memset(vis,0,sizeof(vis));
   for(RG int i=0;i<n;i++) if(!vis[i][0]) ans+=dfs(i,0,-1);
   printf("%d %d %d\n",ans/2333,m-ans%2333,ans%2333);
}

int main(){ T=gi(); while(T--){ init(); work(); } return 0; }

转载于:https://www.cnblogs.com/Hero-of-someone/p/7657065.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值