Codeforces Round #408 (Div. 2)-C. Bank Hacking-(三种方法)分类讨论,二分,集合

33 篇文章 0 订阅
15 篇文章 0 订阅

补题速度太慢了,这样可不行啊。
代码里都有解释。
看别人代码有三种写法
set,分类,和二分。
这是个是用分类思想写的,

#include <bits/stdc++.h>
using namespace std;
/* 一开始我以为是图论的,但是他说有直接相邻的和间接相邻的,
我就有点蒙蔽了。
题意也没看完全明白,要求他的下一次hack的银行附近一定也有被hack过的。
要求他不能跳来跳去的hack,那样的话好像更难。
后来才发现是个思维题。
具体 思路就是分情况。
假设最大的点是m
1 m只有一个,所有的m-1都和m相连,为m
2  m只有一个,不是所有的m-1都和m相连, 为m+1
3 m有好多,但是有一个点连接着所有的m(第二种情况自然符合哦),(为m+1);
4 其他情况就是m+2了,(即m有好多个,并且没有被一个点都连接着)
要看清题啊,不然真的很没头绪
http://codeforces.com/contest/796/problem/C
一个狗要袭击银行。狗有一个攻击力,每个银行都有一个防卫值,当这个银行被攻击以后,与这个银行相邻的和相邻的相邻的都会加一个值。
只有当狗的攻击力大于银行才可以hack,注意狗hack银行的特点
1 每个银行只能hack一次
2 除了第一个,被hack银行要和hack过的银行相邻。
3  要能打的过。攻击力大于防卫值
原题可能不是这样描述的,不过意思一样。
开始以为是图论,真是too young too simple,没想出来


*/
int a[300006];
vector <int > G[300006];
int main()
{   int m=-0x3f3f3f3f;
   int xx;
    int aa,b;
     scanf("%d",&xx);
     for(int i=1;i<=xx;i++)
     {     scanf("%d",&a[i]);
            m=max(m,a[i]);
     }
   for(int i=1;i<=xx-1;i++)
   {   scanf("%d%d",&aa,&b);
        G[aa].push_back(b);
        G[b].push_back(aa);
   }
    int sum1=0;//统计是m的数量
     int summ=0;//统计是m-1的数量
     for(int i=1;i<=xx;i++)
     {   if(a[i]==m)
          summ++;
          else if(a[i]==m-1)
             sum1++;
     }
     if(summ==1)
     {   int i;
        int k=0;
         for( i=1;i<=xx;i++)
           if(a[i]==m)
               break;
         for(int x=0;x<G[i].size();x++)
         {   int y=G[i][x];
             if(a[y]==m-1)
               k++;

         }
         if(k==sum1)
         {cout<<m<<endl;
        return 0;
         }
         else
         {  cout<<m+1<<endl;
           return 0;

         }

     }
    else
    {   int kk=0;
        for(int i=1;i<=xx;i++)
         {  kk=0;
              if(a[i]==m)
              kk++;
             for(int j=0;j<G[i].size();j++)
             {  if(a[G[i][j]]==m)
                     kk++;
             }
             if(kk==summ)
             {  cout<<m+1<<endl;
                  return 0;
             }

         }
               cout<<m+2<<endl;
                return 0;




    }

    return 0;
}

第二种方法是用二分,明显比第一种好理解,也更有可能想出来。
具体见代码
写了好久,有以下错误,局部变量与全局变量的冲突
2 应该把mid当成目标值。

#include <bits/stdc++.h>
using namespace std;
/*
 这道题我终于会了,看了好久了,
 关键二分模拟的是过程,和思维不一样(说实话,二分)

*/
//typedef long long LL;
const int maxn=300006;
struct Node
{    int u,v,next;
}node[maxn*2];
int a[maxn];
int t[maxn];
int m;
int len;
int head[maxn];
void add(int u,int v)
{   node[len].u=u;
    node[len].v=v;
    node[len].next=head[u];
    head[u]=len++;
}
int gg(int x)
{   int tol=0;

    for(int i=1;i<=m;i++)
      {   if(a[i]>x)
            return 0;
          t[i]=a[i]+2;
          if(t[i]>x)
            tol++;//最多增长两次,而这些比他还大,
            //所以要不 第一次就干掉这些大佬,或者第二次干掉。
      }
      if(tol==0) return true;
      if(m==1) return true;
     for(int i=1;i<=m;i++)
     {   int li=tol;
         if(t[i]>x)
         li--;
         for(int j=head[i];j!=-1;j=node[j].next)
         {   if(node[j].v==i) continue;
             if(t[node[j].v]==x+1)
               li--;
         }
         if(li==0)
             return true;
     }
     //上面那个for看了老子好几天,原来就是枚举的时候聪明点啊
      return false;
}
/*bool gg(int  x)
{
    for(int i=1;i<=m;i++)
        t[i] = a[i];
    int cnt = 0;
    for(int i=1;i<=m;i++)
    {
        if(a[i] > x)
            return false;
        t[i]+=2;
        if(t[i] > x)
            cnt++;
    }
    if(m == 1)
        return true;
    if(cnt == 0)
        return true;
    for(int i=1;i<=m;i++)
    {
        int tmp = cnt;
        if(t[i] > x)
            tmp--;
        for(int j=head[i];j!=-1;j=node[j].next)
        {
            int xx = node[j].v;
            if(xx == i)
                continue;
            if(t[xx] == x + 1)
                tmp--;
        }
        if(tmp == 0)
            return true;
    }
    return false;
}*/
int solve(int big,int sma)
{    int h=big;
     int l=sma;
     int mid;
     int ans=l;
     while(h>=l)
     {    mid=(h+l)/2;
         if(gg(mid))
         {   ans=mid;
            h=mid-1;
         }
         else
            l=mid+1;
     }
 return ans;
}
/*int solve(int r,int l)
{
   int ans = l;
    while(l <= r)
    {
        int mid = (l + r) / 2;
        if(gg(mid))
        {
            ans = mid;
            r = mid - 1;
        }
        else
            l = mid + 1;
    }
    return ans;
}*/
int main()
{  std::ios::sync_with_stdio(false);
    cin.tie(0);
   int b1,b;
   int big=-1;
   int sma=0x3f3f3f3f;
   cin>>m;
   memset(head,-1,sizeof(head));
   len=0;
   for(int i=1;i<=m;i++)
       {cin>>a[i];
        big=max(big,a[i]);
        sma=min(sma,a[i]);
       }
    for(int x=1;x<=m-1;x++)
   {    cin>>b1>>b;
       add(b1,b);
      add(b,b1);
   }
     cout<<solve(big+m,sma)<<endl;
    return 0;
}

又用了一个方法,就是set,这个题也算set的初体验了。
a过之后在考虑是不是可以用数组把他换掉,后来发现不好办,因为在set里删除数是比较方便的。如果在数组里要用for来找吧。。
还是set比较方便,毕竟红黑树不是白封装的。。
有几个注意的地方,
第一个tle了,因为我弄了两个set,第二个专门用来做处理。极限数组 300000,赋值30000次。。自然tle
第二次也tle了,因为set是自动排序的(在没有重载运算符的情况下),从下到大,所以其实我直接判断最大的就可以了。
恩呢,加油。

#include <bits/stdc++.h>
/*用set写,set教学写法
 set的建立有三种方法
 1 通过insert函数来添加
 2 通过集合的拷贝
 3 通过对数组的拷贝,如果不知道大小可以定义为
  sizeof(a)/sizeof(int);如果里面是int的话。
  具体思路是模拟,把每一个点当做第一次
*/
const int maxn=300006;
using namespace std;
int main()
{    //std::ios::sync_with_stdio(false);
    multiset<int>set1;
    vector <int>G[maxn];
    int a[maxn];
    int n;
    int b,c;
    scanf("%d",&n);
   for(int i=1;i<=n;i++)
   {   scanf("%d",&a[i]);
      set1.insert(a[i]);
   }
   for(int i=1;i<n;i++)
   {  scanf("%d%d",&b,&c);
      G[c].push_back(b);
      G[b].push_back(c);
   }
   int all=0x3f3f3f3f;
   int sum=0;
    multiset<int>::iterator it;
  for(int i=1;i<=n;i++)
  {   //multiset<int>set1(set2);
      set1.erase(set1.find(a[i]));
      sum=a[i];
      for(int x=0;x<G[i].size();x++)
      {  int s=G[i][x];
         set1.erase(set1.find(a[s]));//find是发现一个
         sum=max(sum,a[s]+1);
      }
      //这一点卡了,如果用set进行遍历

      int k=0;
     if(!set1.empty()){
            it=set1.end();
            sum=max(sum,*(--it)+2);//由于ms.end()指向最后一位,
            //而不是最后一个元素所以--it;因为multiset默认从小到大排列,比最大的就行。
        }
       set1.insert(a[i]);
         for(int t=0;t<G[i].size();t++)
         {   set1.insert(a[G[i][t]]);

         }
      //cout<<sum<<endl;
     all=min(sum,all);
  }
 printf("%d\n",all);

    return 0;
}

附赠福利,这样就知道了为啥 先找到地址,再删除的
set的各成员函数列表如下:

c++ stl容器set成员函数:begin()–返回指向第一个元素的迭代器

c++ stl容器set成员函数:clear()–清除所有元素

c++ stl容器set成员函数:count()–返回某个值元素的个数

c++ stl容器set成员函数:empty()–如果集合为空,返回true

c++ stl容器set成员函数:end()–返回指向最后一个元素的迭代器

c++ stl容器set成员函数:equal_range()–返回集合中与给定值相等的上下限的两个迭代器

c++ stl容器set成员函数:erase()–删除集合中的元素//(给位置。。。)

c++ stl容器set成员函数:find()–返回一个指向被查找到元素的迭代器

c++ stl容器set成员函数:get_allocator()–返回集合的分配器

c++ stl容器set成员函数:insert()–在集合中插入元素

c++ stl容器set成员函数:lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器

c++ stl容器set成员函数:key_comp()–返回一个用于元素间值比较的函数

c++ stl容器set成员函数:max_size()–返回集合能容纳的元素的最大限值

c++ stl容器set成员函数:rbegin()–返回指向集合中最后一个元素的反向迭代器

c++ stl容器set成员函数:rend()–返回指向集合中第一个元素的反向迭代器

c++ stl容器set成员函数:size()–集合中元素的数目

c++ stl容器set成员函数:swap()–交换两个集合变量

c++ stl容器set成员函数:upper_bound()–返回大于某个值元素的迭代器

c++ stl容器set成员函数:value_comp()–返回一个用于比较元素间的值的函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值