執筆時レート:水色
問題文→https://atcoder.jp/contests/abc301/tasks/abc310_d
公式解説→https://atcoder.jp/contests/abc301/tasks/abc310_d/editorial
目次
問題
個の商品がある.
各商品には価格と機能が以下のように定義されている.
- 商品 の価格は
- 商品 の機能は 個あり, 番目の機能は 以上 以下の整数 として表される
このとき,以下の条件を全て満たすような商品 ,商品 の組は存在するか?
- 商品 は商品 がもつ機能をすべてもつ
- であるか,商品 は商品 にない機能を つ以上もつ
制約
- 入力は全て整数
解説
まずは全探索を考えるのが基本.
全ての組 について,問題文の条件を全て満たすものがあるか調べる.
問題文の条件が何を意味しているのかが分からないときは,言葉で表現してみると分かりやすくなるかも.
これは「商品 の方が安いか,同じ価格」.
- 商品 は商品 のもつ機能をすべてもつ
これはそのままの意味.
「商品 は商品 以上に多機能」とも言い換えられる.
- であるか,商品 は商品 にない機能を つ以上もつ
価格については「商品 の方が安い」ということ.
機能についてはそのままの意味だが,二つ目の条件を満たしているときは,「商品 の機能 商品 の機能 」,つまり「商品 の方が多機能」ということになる.
このような3条件を全て満たす組があるか調べれば良い.
計算量はどうだろう.
調べる組み合わせの数は で,ある組 について各条件の判定に,
- の比較をすれば良いので
- 商品 の各機能が商品 に含まれているか素朴に調べて
- 価格の比較も機能数の比較も なので,合わせて
これだと全体の計算量は, となり,今回の制約だとまあ間に合うかな,という感じ.(C++の処理の速さを信じて)
他の言語だと間に合わないかもしれないし,定数倍がこわいときは,set(集合に対して所属判定を で行える構造体)を使えば高速化が図れる.
具体的には,判定では がボトルネックになっているので,商品 の機能をまとめたsetに対して商品 の各機能が含まれているかを調べれば,判定は ,全体で になる.
さらに,各商品について「あり得る 個の機能のうち持っているものにフラグを立てる」という管理をすれば,二つ目の条件の判定を ,全体で に落とせる.
実装
解法.余裕で間に合う.
提出
#include <bits/stdc++.h> using namespace std; int main(){ // 入力を受け取る int n,m; cin>>n>>m; vector<int> p(n); vector<vector<int>> func(n); for(int i=0; i<n; i++) { cin>>p[i]; int c;cin>>c; for(int j=0; j<c; j++) { int f; cin>>f; func[i].push_back(f); } } // 判定 for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { if(p[i]>=p[j]){ // 一つ目の条件を満たしている // 二つ目の条件「商品jが商品iの機能をすべて含んでいるか」を調べる bool inc=true; // 商品iの機能をすべて含むか? for(auto fi: func[i]) { // 商品iのある機能fiが商品jにあるか? bool exist=false; for(auto fj: func[j]) { if(fi==fj) exist=true; } // 無ければすべて含んでいないということ if(!exist) inc=false; } if(!inc) continue; // ここまでで二つ目の条件を満たしている // 最後の条件を満たすか調べる if(p[i]>p[j] || func[i].size()<func[j].size()) { // すべての条件を満たしている! cout<<"Yes"<<'\n'; return 0; } } } } // すべての組を調べても無かった cout<<"No"<<'\n'; return 0; }
setを使った 解法.
提出
#include <bits/stdc++.h> using namespace std; int main(){ // 入力を受け取る int n,m; cin>>n>>m; vector<int> p(n); vector<set<int>> func(n); for(int i=0; i<n; i++) { cin>>p[i]; int c;cin>>c; for(int j=0; j<c; j++) { int f; cin>>f; func[i].insert(f); } } // 判定 for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { if(p[i]>=p[j]){ // 一つ目の条件を満たしている // 二つ目の条件「商品jが商品iの機能をすべて含んでいるか」を調べる bool inc=true; // 商品iの機能をすべて含むか? for(auto fi: func[i]) { // 商品iのある機能fiが商品jに無ければ,すべて含んでいないということ if(!func[j].count(fi)) inc=false; } if(!inc) continue; // ここまでで二つ目の条件を満たしている // 最後の条件を満たすか調べる if(p[i]>p[j] || func[i].size()<func[j].size()) { // すべての条件を満たしている! cout<<"Yes"<<'\n'; return 0; } } } } // すべての組を調べても無かった cout<<"No"<<'\n'; return 0; }
解法.
提出
#include <bits/stdc++.h> using namespace std; int main(){ // 入力を受け取る int n,m; cin>>n>>m; vector<int> p(n); vector<int> c(n); vector<vector<bool>> func(n,vector<bool>(m)); for(int i=0; i<n; i++) { cin>>p[i]>>c[i]; for(int j=0; j<c[i]; j++) { int f; cin>>f; f--; func[i][f]=true; } } // 判定 for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { if(p[i]>=p[j]){ // 一つ目の条件を満たしている // 二つ目の条件「商品jが商品iの機能をすべて含んでいるか」を調べる bool inc=true; // 商品iの機能をすべて含むか? for(int k=0; k<m; k++) { // 機能kを商品iはもっているが商品jはもっていなければ,すべて含んでいないということ if(func[i][k] && !func[j][k]) inc=false; } if(!inc) continue; // ここまでで二つ目の条件を満たしている // 最後の条件を満たすか調べる if(p[i]>p[j] || c[i]<c[j]) { // すべての条件を満たしている! cout<<"Yes"<<'\n'; return 0; } } } } // すべての組を調べても無かった cout<<"No"<<'\n'; return 0; }