# APCS 2017/10 題解 ## ==邏輯運算子== ### [題目](https://zerojudge.tw/ShowProblem?problemid=c461) ### 核心 條件判斷 ### 思路 ```c++= #include<bits/stdc++.h> using namespace std; int main() { int a,b,c; bool can=false; scanf("%d%d%d",&a,&b,&c); a=(a!=0?1:0),b=(b!=0?1:0); if((a&&b)==c) can=true,printf("AND\n"); if((a||b)==c) can=true,printf("OR\n"); if((a^b)==c) can=true,printf("XOR\n"); if(!can) printf("IMPOSSIBLE\n"); } ``` ## ==交錯字串== ### [題目](https://zerojudge.tw/ShowProblem?problemid=c462) ### 核心 vector ### 思路 將連續大(小)寫子串列長度存入segment,接著遍歷segment,初始設num=0,當子串列長度>=k時num=1,再找連續的長度k子串列,若找到長度> k的子串列,則更新答案並重計num,若找到長度< k的子串列,則更新答案並設num=0。 ```c++= #include<bits/stdc++.h> using namespace std; int k,ans=0; string str; vector<int> segment; int main() { ios::sync_with_stdio(0),cin.tie(0); cin>>k>>str; int tmp=1; for(int i=1;i<str.size();i++) { if(isupper(str[i])==isupper(str[i-1])) tmp++; else { segment.push_back(tmp); tmp=1; } }segment.push_back(tmp); int num=0; for(int i=0;i<segment.size();i++) { if(num>0) { if(segment[i]==k) num++; else if(segment[i]>k) { ans=max(ans,(num+1)*k); num=1; } else ans=max(ans,num*k),num=0; } else { if(segment[i]>=k) num=1; } } ans=max(ans,num*k); cout<<ans<<endl; } ``` ## ==樹狀圖分析== ### [題目](https://zerojudge.tw/ShowProblem?problemid=c463) ### 核心 dfs、tree ### 思路1(bottom-up) 最差複雜度接近O(n^2^),思路簡單但不建議這樣寫。 從子節點開始向上dfs,每次更新父節點的高度。 最後再取總和。 ```c++= #include<bits/stdc++.h> using namespace std; #define N 100005 int n,k; int parent[N],h[N]={0}; vector<int> child[N]; long long ans=0; void dfs(int u) { if(parent[u]==-1) return; h[parent[u]]=max(h[parent[u]],h[u]+1); dfs(parent[u]); } int main() { memset(parent,-1,sizeof(parent)); scanf("%d",&n); for(int v=1;v<=n;v++) { int u; scanf("%d",&k); while(k--) { scanf("%d",&u); child[v].push_back(u); parent[u]=v; } } int root; for(int i=1;i<=n;i++) { if(child[i].size()==0) dfs(i); if(parent[i]==-1) root=i; } for(int i=1;i<=n;i++) ans+=h[i]; printf("%d\n%lld",root,ans); } ``` ### 思路2(top-down) 複雜度O(n)。 每個節點存子節點的最深深度far[id],dep為當前深度,far[id]-dep就是最大高度。 ```c++= #include<bits/stdc++.h> using namespace std; #define N 100005 int n,k,u,far[N]; vector<int> child[N]; unordered_set<int> S; long long ans=0; void dfs(int id,int dep) { if(child[id].size()==0) { far[id]=dep; return; } for(int u:child[id]) { dfs(u,dep+1); far[id]=max(far[id],far[u]); } ans+=far[id]-dep; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) S.insert(i); for(int v=1;v<=n;v++) { scanf("%d",&k); while(k--) { scanf("%d",&u); child[v].push_back(u); S.erase(u); } } dfs(*S.begin(),0); printf("%d\n%lld",*S.begin(),ans); } ``` ## ==物品堆疊== ### [題目](https://zerojudge.tw/ShowProblem?problemid=c471) ### 核心 貪心、加權排序 ### 思路 若有A、B兩物品,A在下時取A要消耗 w(B) * f(A) 能量,B在下時取B要消耗 w(A) * f(B) 能量,小的應該在上面,排序的規則出來了。 ```c++= #include<bits/stdc++.h> using namespace std; #define N 100005 int n; struct good { int w,f; }goods[N]; bool cmp(good &a,good &b) { return a.w*b.f<a.f*b.w; } int main() { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&goods[i].w); for(int i=0;i<n;i++) scanf("%d",&goods[i].f); sort(goods,goods+n,cmp); long long cnt=goods[0].w,ans=0; for(int i=1;i<n;i++) { ans+=cnt*goods[i].f; cnt+=goods[i].w; } printf("%lld\n",ans); } ```