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()–返回一個用於比較元素間的值的函式