[codeforces#438 E题]Policeman and a Tree

基于边的树形DP+DP转移一个神奇的二分。

二分要从罪犯的角度去理解,之前一直在纠结一个警察和罪犯的极大极小博弈,没想到可以有这么一个神奇的二分。

写完AC,但感觉理解得不够深刻。

#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
#define INF 0x7f000000
#define N 55
struct EDGE{
  int u,v,w;
  EDGE(int u=0,int v=0,int w=0):u(u),v(v),w(w){}
};
vector<EDGE> e;
vector<int> g[N];
int f[N*2][N][N];
int n,m;
int ccnt[N];
bool isleaf[N];
int st;
void read(){
  scanf("%d",&n);
  for (int i=1;i<n;i++){
    int u,v,w;
    scanf("%d%d%d",&u,&v,&w);
    e.push_back(EDGE(u,v,w));
    e.push_back(EDGE(v,u,w));
    g[u].push_back(e.size()-2);
    g[v].push_back(e.size()-1);
  }
  scanf("%d",&st);
  scanf("%d",&m);
  memset(ccnt,0,sizeof(ccnt));
  for (int i=1;i<=m;i++){
    int x;
    scanf("%d",&x);
    ccnt[x]++;
  }
  memset(isleaf,0,sizeof(isleaf));
  for (int i=1;i<=n;i++)
    if (g[i].size()==1)
      isleaf[i]=true;
}
int dfs(int u,int p){
  for (int i=0;i<g[u].size();i++){
    EDGE ed=e[g[u][i]];
    if (ed.v==p)continue;
    ccnt[u]+=dfs(ed.v,u);
  }
  //printf("ccnt[%d]=%d\n",u,ccnt[u]);
  return ccnt[u];
}
int dp(int eid,int s,int s2){ //沿着eid走
  int &fval=f[eid][s][s2];
  if (fval<INF) return fval;
  if (s==0) return 0;
  if (s2==0) exit(-2);

  EDGE ed=e[eid];
  int u=ed.u;int v=ed.v;int w=ed.w;
  //到达叶子节点//抓住s2个罪犯,往回走
  if (isleaf[v]){
     fval=w+dp(eid^1,s-s2,s-s2);
     //printf("fval=%d\n",fval);
     return fval;
  }
  //非叶子节点
  //神奇的二分,找到T,表示警察能抓到所有罪犯最小时间
  int l=1,r=N*N*N;
  while(l<r){
     int T=(l+r)/2;
     int sum=0;  //不让警察在T时间内抓住的最大罪犯个数
     for (int i=0;i<g[v].size();i++){
            int v2=e[g[v][i]].v;
            int w2=e[g[v][i]].w;
            if (v2==u) continue; //不走回头
            //找到尽量大的b,使得当走e[g[u][i]]这条边,可以在<=T的时间内抓住所有罪犯
            int b;
            for(b=1;b<=s2;b++){
                if (dp(g[v][i],s,b)<=T)
                    break;
            }
            if (b>s2)
                sum=s2;
            else
               sum+=b-1;
            if (sum>=s2) break;
     }
     if (sum>=s2) //存在一种方案使得警察无法在T时间内抓到所有人
        l=T+1;
     else
        r=T;
  }
  fval=w+l;
  return fval;
}
int main(){
  read();
  dfs(st,0);
  memset(f,127,sizeof(f));
  int ans=INF;
  for (int i=0;i<g[st].size();i++){
    EDGE ed=e[g[st][i]];
    if (ccnt[ed.v])
        ans=min(ans,dp(g[st][i],m,ccnt[ed.v]));
  }
  printf("%d\n",ans);
  //printf("%d",dp(3,1,1));
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值