1. 程式人生 > >Codeforces Round #408 (Div. 2)-C. Bank Hacking-(三種方法)分類討論,二分,集合

Codeforces Round #408 (Div. 2)-C. Bank Hacking-(三種方法)分類討論,二分,集合

補題速度太慢了,這樣可不行啊。
程式碼裡都有解釋。
看別人程式碼有三種寫法
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()–返回一個用於比較元素間的值的函式