next_permutation の逆 辞書順の大きい順番
comp を指定する
https://cpprefjp.github.io/reference/algorithm/next_permutation.html
上のサイトを見ると, 第三引数に比較関数を取れるので, 比較関数を作れば辞書順の逆でやることも可能.
プログラムは以下の通りになる.
#include <bits/stdc++.h> using namespace std; bool comp(int i, int j) { return i > j; } int main(void) { vector<int> v{1, 2, 3}; sort(v.begin(), v.end(), comp); do { for(auto u : v) cout << u << " "; cout << endl; }while( next_permutation(v.begin(), v.end(), comp) ); return 0; }
出力は以下の通りになる.
3 2 1 3 1 2 2 3 1 2 1 3 1 3 2 1 2 3
prev_permutation がありました.
#include <bits/stdc++.h> using namespace std; int main(void) { vector<int> v{1, 2, 3}; sort(v.begin(), v.end()); reverse(v.begin(), v.end()); do { for(auto u : v) cout << u << " "; cout << endl; }while( prev_permutation(v.begin(), v.end()) ); return 0; }
上のプルグラムの出力はcompのと一致します.
aoj 2827 凸多角形柱工業都市
問題概要
スタートからゴールまで最短距離で移動したい. 影を通れば距離には加算されない.
(2017 ICPC国内模擬 E)
解法
建物とその影を考えて多角形を作り, N個の多角形どうしの距離を計算して辺を作り, ダイクストラで最短距離を計算すれば良い.
建物の内部を通れないという制約があるが, 建物の端を通ることはできるので, 建物自体もいれて多角形を作れば良い. また, 建物の座標とその影の座標から多角形の座標を得るには, 凸法を使えば楽かも.
ミス
startとgoalの辺を追加していなくて, バグらせた.
コード
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef vector<int> vint; typedef pair<int,int> pint; typedef vector<pint> vpint; #define rep(i,n) for(int i=0;i<(n);i++) #define REP(i,n) for(int i=n-1;i>=(0);i--) #define reps(i,f,n) for(int i=(f);i<(n);i++) #define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++) #define all(v) (v).begin(),(v).end() #define eall(v) unique(all(v), v.end()) #define pb push_back #define mp make_pair #define fi first #define se second #define chmax(a, b) a = (((a)<(b)) ? (b) : (a)) #define chmin(a, b) a = (((a)>(b)) ? (b) : (a)) const int MOD = 1e9 + 7; const int INF = 1e9; const ll INFF = 1e18; const double EPS = 1e-10; const double PI = 3.141592653589793; template<class T> bool eq(T a, T b){ return abs(a - b) < EPS; } class Point { // 点 public: double x, y; Point(double x = 0, double y = 0):x(x), y(y){} Point operator + (Point p) const { return Point(x + p.x, y + p.y); } Point operator - (Point p) const { return Point(x - p.x, y - p.y); } Point operator * (double a) const { return Point(a * x, a * y); } Point operator / (double a) const { return Point(x / a, y / a); } double abs() const { return sqrt(norm()); } double norm() const { return x * x + y * y; } // bool operator < (const Point &p) const { return x != p.x ? x < p.x : y < p.y; } bool operator < (const Point &p) const { // 誤差を許容して比較 return x + EPS < p.x || (eq<double>(x, p.x) && y + EPS < p.y); } bool operator == (const Point &p) const { return (eq<double>(x, p.x) && eq<double>(y, p.y)); } }; using Vector = Point; using Polygon = vector<Point>; // 多角形 double dot(const Vector& a, const Vector& b) { return a.x * b.x + a.y * b.y; } // ベクトルaとbの内積 double cross(const Vector& a, const Vector& b) { return a.x * b.y - a.y * b.x; } // ベクトルaとbの外積 double length2(const Point& a) { return a.norm(); } // 通常の長さの2乗 double length(const Point& a) { return a.abs(); } // 通常の長さ Point rotationalTransfer(Point c, double r, double deg) { // cを中心として半径rの円周上のdeg度の位置座標 double rad = PI * deg / 180.0; return c + Point(cos(rad), sin(rad)) * r; } // (x, y, z) の点を光源(xy座標での角度がtheta度, xy平面からz方向への角度がphi度の時の)からてらした時の影のxy座標 Point Shadow(double x, double y, double z, double theta, double phi) { theta = PI * theta / 180.0, phi = PI * phi / 180.0; return Point(x - z / tan(phi) * cos(theta), y - z / tan(phi) * sin(theta)); } enum ccw_t { COUNTER_CLOCKWISE = 1, // p0->p1 反時計回りの方向にp2 CLOCKWISE = -1, // p0->p1 時計回りの方向にp2 ONLINE_BACK = 2, // p2->p0->p1 の順で直線上でp2 ONLINE_FRONT = -2, // p0->p1->p2 の順で直線上p2 ON_SEGMENT = 0, // p0->p2->p1 の順で線分p0p1上にp2 }; ccw_t ccw(Point p0, Point p1, Point p2) { Vector a = p1 - p0, b = p2 - p0; if ( cross(a, b) > EPS ) return COUNTER_CLOCKWISE; if ( cross(a, b) < -EPS ) return CLOCKWISE; if ( dot(a, b) < -EPS ) return ONLINE_BACK; if ( a.norm() < b.norm() ) return ONLINE_FRONT; return ON_SEGMENT; } class Segment { //線分 public: Point p1, p2; Segment(){} Segment(Point p1, Point p2):p1(p1), p2(p2){} }; using Line = Segment; // *** 多角形 *** // IN := 2, ON := 1, OUT := 0 vector<Segment> getPolygonSegument(const Polygon& p) { //多角形の点から多角形の辺を求める vector<Segment> ret; rep(i, p.size() - 1) ret.push_back(Segment(p[i], p[i + 1])); ret.push_back(Segment(p[p.size() - 1], p[0])); return ret; } int contains(const Polygon& g, const Point& p){ // 多角形gの中に点pが含まれているか int n = g.size(); bool x = false; for (int i = 0; i < n; ++i) { Point a = g[i] - p, b = g[(i + 1) % n] - p; if ( abs(cross(a, b)) < EPS && dot(a, b) < EPS ) return 1; if (a.y > b.y) swap(a, b); if (a.y < EPS && EPS < b.y && cross(a, b) > EPS ) x = !x; } return (x ? 2 : 0); } Polygon andrewScan(Polygon s) { // 凸包(最も左にある頂点から) Polygon u, l; if (s.size() < 3) return s; sort(s.begin(), s.end()); // x, yを基準に昇順ソート // xが小さいものから2つ u に追加 u.push_back(s[0]), u.push_back(s[1]); // x が大きいものから2つ1に追加 l.push_back(s[s.size() - 1]), l.push_back(s[s.size() - 2]); // 凸包の上部を生成 for (int i = 2; i < s.size(); i++) { for (int n = u.size(); n >= 2 && ccw(u[n - 2], u[n - 1], s[i]) != CLOCKWISE; n--){ u.pop_back(); } u.push_back(s[i]); } // 凸包の下部を生成 for (int i = s.size() - 3; i >= 0; i--) { for (int n = l.size(); n >= 2 && ccw(l[n - 2], l[n - 1], s[i]) != CLOCKWISE; n--){ l.pop_back(); } l.push_back(s[i]); } // 時計回りになるように凸包の点の列を生成 reverse(l.begin(), l.end()); for (int i = u.size() - 2; i >= 1; i--) l.push_back(u[i]); return l; } // *** 線分の交差判定 *** bool intersect(const Point& p1, const Point& p2, const Point& p3, const Point& p4) { return ( ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0 ); } bool intersect(const Segment& s1, const Segment& s2) { // 交差していたらtrue return intersect(s1.p1, s1.p2, s2.p1, s2.p2); } //*** 線分の交点 *** Point getCrossPoint(Segment s1, Segment s2) { // 線分の交点が存在するから調べた後つかうこと Vector base = s2.p2 - s2.p1; double d1 = abs(cross(base, s1.p1 - s2.p1)), d2 = abs(cross(base, s1.p2 - s2.p1)); double t = d1 / (d1 + d2); return s1.p1 + (s1.p2 - s1.p1) * t; } // *** 距離 *** double getDistance(Point& a, Point& b) { // 点aと点bの距離 return length(a - b); } double getDistanceLP(Line& l, Point& p) { // 直線sと点pの距離 return length(cross(l.p2 - l.p1, p - l.p1) / length(l.p2 - l.p1)); } double getDistanceSP(Segment s, Point p) { // 線分sと点pの距離 if( dot(s.p2 - s.p1, p - s.p1) < EPS ) return length(p - s.p1); if( dot(s.p1 - s.p2, p - s.p2) < EPS ) return length(p - s.p2); return getDistanceLP(s, p); } double getDistanceSS(const Segment& s1, const Segment& s2) { // 線分s1と線分s2の交点 if( intersect(s1, s2) ) return 0.0; //交わっているとき return min(min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2)), min(getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2))); } double getDistancePolP(const Polygon& pol, const Point& p) { // 多角形polと点pの距離 if(contains(pol, p) != 0) return 0.0; // 点が多角形の内部に含まれている double ret = 1e9; for(Segment& u : getPolygonSegument(pol)) ret = min(ret, getDistanceSP(u, p)); return ret; } double getDistancePolPol(const Polygon& p1, const Polygon& p2) { // 多角形p1とp2の距離 for(const Point& p : p1) if(contains(p2, p) != 0) return 0.0; // p1の点が多角形p2の中に含まれている for(const Point& p : p2) if(contains(p1, p) != 0) return 0.0; // p2の点が多角形p1の中に含まれている double ret = 1e9; for(const Segment& u : getPolygonSegument(p1))for(const Segment& v : getPolygonSegument(p2)) { ret = min(ret, getDistanceSS(u, v)); } return ret; } class Rectangle { // 長方形 public: // 3 2 // 0 1 (反時計回りに長方形の頂点をいれること) vector<Point> p; // 点を順番にいれること Rectangle(vector<Point>&p):p(p) { rep(i, 3) reps(j, i + 1, 4) { //適当な順番にいれても大丈夫なように? int cnt = 0; rep(k, 4) if(k != i && k != j) { cnt += ccw(p[i], p[j], p[k]) == COUNTER_CLOCKWISE; } if(cnt == 2) { swap(p[i + 1], p[j]); break; } } } bool intersect(const Segment& s) { // 線分sと長方形の少なくとも1辺が交差していればtrue bool flag = false; rep(i, 4) flag |= ::intersect(s, Segment(p[i], p[(i + 1) % 4])); return flag; } bool contain(const Point& pp) { // 点ppが長方形内に含まれれば(辺を含まない)true bool flag = true; rep(i, 4) flag &= ccw(p[i], p[(i + 1) % 4], pp) == COUNTER_CLOCKWISE; return flag; } bool contain(const Segment& s) { // 線分sが長方形内に含まれれば(辺を含まない)true return contain(s.p1) && contain(s.p2); } }; const int MAX_N = 210; using TYPE = double; // 距離の型を入れる vector<pair<int, TYPE> > G[MAX_N]; vector<TYPE> dijkstra(int start){ vector<TYPE> dist(MAX_N, INFF); dist[start] = 0;//dist[i] := start->iまでの最短距離 priority_queue<pair<TYPE, int>, vector<pair<TYPE, int> >, greater<pair<TYPE, int> > > que; que.push(make_pair(0, start)); while(!que.empty()){ TYPE cost; int u;//今までにかかった時間 現在の頂点 cost = que.top().first, u = que.top().second; que.pop(); if(dist[u] < cost) continue; for (auto tmp : G[u]){ int v = tmp.first; TYPE time = tmp.second;//隣接する頂点 その頂点まで行く時間 if(dist[v] > dist[u] + time){//u->v dist[v] = dist[u] + time; que.push(make_pair(dist[v], v)); } } } return dist; } int main(void) { while(1) { int N; scanf("%d", &N); if(N == 0) break; int NV[110], H[110]; vector<vector<pair<double, double>>> p; rep(i, N){ scanf("%d %d", &NV[i], &H[i]); vector<pair<double, double>> tmp; rep(j, NV[i]){ double x, y; scanf("%lf %lf", &x, &y); tmp.pb(mp(x, y)); } p.pb(tmp); } double theta, phi; scanf("%lf %lf", &theta, &phi); double Sx, Sy, Tx, Ty; scanf("%lf %lf %lf %lf", &Sx, &Sy, &Tx, &Ty); Point start(Sx, Sy), goal(Tx, Ty); vector<Polygon> v; rep(i, N){ Polygon t; for(auto u : p[i]) t.pb(Shadow(u.fi, u.se, H[i], theta, phi)); for(auto u : p[i]) t.pb(Point(u.fi, u.se)); Polygon tt = andrewScan(t); v.pb(tt); } /* for(auto u : v) { for(auto k : u) printf("%f %f, ", k.x, k.y); printf("\n"); } */ rep(i, MAX_N) G[i].clear(); rep(i, v.size())rep(j, v.size()){ if(i == j) continue; int s = i + 1, t = j + 1; auto dis = getDistancePolPol(v[i], v[j]); G[s].pb(mp(t, dis)), G[t].pb(mp(s, dis)); } rep(i, v.size()){ auto dis = getDistancePolP(v[i], start); G[0].pb(mp(i + 1, dis)), G[i + 1].pb(mp(0, dis)); } rep(i, v.size()){ auto dis = getDistancePolP(v[i], goal); G[N + 1].pb(mp(i + 1, dis)), G[i + 1].pb(mp(N + 1, dis)); } G[0].pb(mp(N + 1, getDistance(start, goal))), G[N + 1].pb(mp(0, getDistance(start, goal))); auto ret = dijkstra(0); printf("%.9f\n", ret[N + 1]); } return 0; }
aoj 1318 Long Distance Taxi
問題概要
グラフがあたえられる. 各頂点のいくつかはガソリンステーションでガソリンを入れることができる. ガソリンがなくならずに, スタートからゴールに行くまでの最短距離を求める.
またLPGのタンクの最大容量もあたえられる. 車の燃費は10km/Lである.
解法
dist[i][j] := 頂点iまで残りLPGがjで来た時の最短距離
という状態を拡張ダイクストラで計算していく.
気をつけなければならないところは, 与えられたcapと燃費10km/Lのままだと, 道の距離が1kmの時, タンクから0.1を引かなければならなくなり, 少数がでてしまう.
だから, capを10倍することでこれを回避できる.
ミス
ガソスタがあるときは, そこでガソリンを満タンにしてしまえばいい.
満タン以外にもすこしいれるなどの処理をしてしまったらTLEした. (下のコード参照)
コード
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef vector<int> vint; typedef pair<int,int> pint; typedef vector<pint> vpint; #define rep(i,n) for(int i=0;i<(n);i++) #define REP(i,n) for(int i=n-1;i>=(0);i--) #define reps(i,f,n) for(int i=(f);i<(n);i++) #define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++) #define all(v) (v).begin(),(v).end() #define eall(v) unique(all(v), v.end()) #define pb push_back #define mp make_pair #define fi first #define se second #define chmax(a, b) a = (((a)<(b)) ? (b) : (a)) #define chmin(a, b) a = (((a)>(b)) ? (b) : (a)) const int MOD = 1e9 + 7; const int INF = 1e9; const ll INFF = 1e18; map<string, int> city2num; set<int> LPG; vector<pair<int, int>> G[6010]; int dist[6010][2010]; // dist[i][j] := 頂点iまで残りLPGがjで来た時の最短距離 using tup = tuple<int, int, int>; void dijkstra(int start, int cap){ rep(i, 6010)rep(j, 2010) dist[i][j] = INF; cap *= 10; dist[start][cap] = 0; priority_queue<tup, vector<tup>, greater<tup> > que; // 距離 頂点 残りのLPG que.push(make_tuple(0, start, cap)); while(!que.empty()){ int cost, u, lpg; tie(cost, u, lpg) = que.top(); que.pop(); if(dist[u][lpg] < cost) continue; for (auto& tmp : G[u]){ int v = tmp.first, di = tmp.second; int nlpg = lpg - di; int ncost = cost + di; if(nlpg < 0) continue; if(LPG.count(v)) { /* for (int i = nlpg; i <= cap; ++i){ これだとTLE(9.0s) if(dist[v][i] > ncost) { dist[v][i] = ncost; que.push(make_tuple(ncost, v, i)); } } */ if(dist[v][cap] > ncost) { dist[v][cap] = ncost; que.push(make_tuple(ncost, v, cap)); } }else{ if(dist[v][nlpg] > ncost) { dist[v][nlpg] = ncost; que.push(make_tuple(ncost, v, nlpg)); } } } } } string c1[6010], c2[6010]; int d[6010]; string s[6010]; int main(void) { while(1){ city2num.clear(); LPG.clear(); rep(i, 6010) G[i].clear(); int N, M, cap; scanf("%d %d %d", &N, &M, &cap); if(N == 0 && M == 0 && cap == 0) break; string src, dest; cin >> src >> dest; rep(i, N) cin >> c1[i] >> c2[i] >> d[i]; rep(i, M) cin >> s[i]; set<string> city; rep(i, N) city.insert(c1[i]), city.insert(c2[i]); int cnt = 0; for(auto& u : city) { city2num[u] = cnt++; } rep(i, M) LPG.insert(city2num[s[i]]); rep(i, N) G[city2num[c1[i]]].pb(mp(city2num[c2[i]], d[i])), G[city2num[c2[i]]].pb(mp(city2num[c1[i]], d[i])); int ns = city2num[src], nd = city2num[dest]; dijkstra(ns, cap); ll ans = INF; rep(i, cap * 10 + 1) chmin(ans, dist[nd][i]); if(ans != INF)printf("%lld\n", ans); else printf("-1\n"); } return 0; }
九州大学プログラミングコンテスト2014 H - お風呂は気持ちいい
問題概要
頂点の間の容量が与えられる. 複数のスタートから一つのゴールまで最大どのくらいのものを運ぶことができるか.
解法
G個のスタート地点があるので, スタート地点の頂点を新たに作り, そこから魔導石に近い魔法使いへ辺の容量がINFの辺をはる. あとは与えられた辺をそのままはって, スタート地点からアスナにどれだけ流せるかをフローで求めれば良い.
ミス
最大流の基本的な問題.
コード
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef vector<int> vint; typedef pair<int,int> pint; typedef vector<pint> vpint; #define rep(i,n) for(int i=0;i<(n);i++) #define REP(i,n) for(int i=n-1;i>=(0);i--) #define reps(i,f,n) for(int i=(f);i<(n);i++) #define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++) #define all(v) (v).begin(),(v).end() #define eall(v) unique(all(v), v.end()) #define pb push_back #define mp make_pair #define fi first #define se second #define chmax(a, b) a = (((a)<(b)) ? (b) : (a)) #define chmin(a, b) a = (((a)>(b)) ? (b) : (a)) const int MOD = 1e9 + 7; const int INF = 1e9; const ll INFF = 1e18; const int MAX_V = 510; //必要な頂点数 struct Flow{ struct edge{ int to, cap, rev; }; vector<edge> G[MAX_V];//隣接リスト bool used[MAX_V]; void add_edge(int from, int to, int cap){ G[from].push_back((edge){to, cap, (int)G[to].size()});//from -> to G[to].push_back((edge){from, 0, (int)G[from].size() - 1});//to -> from } //増加パスを探す int dfs(int v, int t, int f){ if(v == t) return f; used[v] = true; for (int i = 0; i < G[v].size(); ++i){ edge &e = G[v][i]; if(!used[e.to] && e.cap > 0){ int d = dfs(e.to, t, min(f, e.cap)); if(d > 0){ e.cap -= d; G[e.to][e.rev].cap += d; return d; } } } return 0; } //sからtへの最大流 int max_flow(int s, int t){ int flow = 0; while(1){ memset(used, 0, sizeof(used)); int f = dfs(s, t, INF); if(f == 0) return flow; flow += f; } } }; int N, M, P, G; int L[12]; int from[510], to[510], cap[510]; int main(void) { cin >> N >> M >> P >> G; rep(i, G) cin >> L[i]; rep(i, N) cin >> from[i] >> to[i] >> cap[i]; Flow f; int start = N + 1; rep(i, G) f.add_edge(start, L[i], INF); rep(i, N) f.add_edge(from[i], to[i], cap[i]); int ret = f.max_flow(start, 0); if(ret < P) printf("No\n"); else printf("Yes\n"); return 0; }
aoj 2402 Milky Way
問題概要
五芒星が与えられる. スタートの星からゴールの星までの移動の最小距離を求める. 星の中は移動距離に入らない.
解法
星と星の距離は, 各星は5つの辺からなっていることから, 5つの辺ぞれぞれを総当りで調べて, その中の最小値が距離になる. このように距離を求めて, ダイクストラを行えば良い.
ミス
点を回転させる部分を間違えてて, 辛かった.
コード
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef vector<int> vint; typedef pair<int,int> pint; typedef vector<pint> vpint; #define rep(i,n) for(int i=0;i<(n);i++) #define REP(i,n) for(int i=n-1;i>=(0);i--) #define reps(i,f,n) for(int i=(f);i<(n);i++) #define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++) #define all(v) (v).begin(),(v).end() #define eall(v) unique(all(v), v.end()) #define pb push_back #define mp make_pair #define fi first #define se second #define chmax(a, b) a = (((a)<(b)) ? (b) : (a)) #define chmin(a, b) a = (((a)>(b)) ? (b) : (a)) const int MOD = 1e9 + 7; const int INF = 1e9; const ll INFF = 1e18; const double EPS = 1e-10; const double PI = 3.141592653589793; template<class T> bool eq(T a, T b){ return abs(a - b) < EPS; } class Point { // 点 public: double x, y; Point(double x = 0, double y = 0):x(x), y(y){} Point operator + (Point p) { return Point(x + p.x, y + p.y); } Point operator - (Point p) { return Point(x - p.x, y - p.y); } Point operator * (double a) { return Point(a * x, a * y); } Point operator / (double a) { return Point(x / a, y / a); } double abs() const { return sqrt(norm()); } double norm() const { return x * x + y * y; } // bool operator < (const Point &p) const { return x != p.x ? x < p.x : y < p.y; } bool operator < (const Point &p) const { // 誤差を許容して比較 return x + EPS < p.x || (eq<double>(x, p.x) && y + EPS < p.y); } bool operator == (const Point &p) const { return (eq<double>(x, p.x) && eq<double>(y, p.y)); } }; using Vector = Point; using Polygon = vector<Point>; // 多角形 double dot(const Vector& a, const Vector& b) { return a.x * b.x + a.y * b.y; } // ベクトルaとbの内積 double cross(const Vector& a, const Vector& b) { return a.x * b.y - a.y * b.x; } // ベクトルaとbの外積 double length2(const Point& a) { return a.norm(); } // 通常の長さの2乗 double length(const Point& a) { return a.abs(); } // 通常の長さ Point rotationalTransfer(Point c, double r, double deg) { // cを中心として半径rの円周上のdeg度の位置座標 double rad = PI * deg / 180.0; return c + Point(cos(rad), sin(rad)) * r; } enum ccw_t { COUNTER_CLOCKWISE = 1, // p0->p1 反時計回りの方向にp2 CLOCKWISE = -1, // p0->p1 時計回りの方向にp2 ONLINE_BACK = 2, // p2->p0->p1 の順で直線上でp2 ONLINE_FRONT = -2, // p0->p1->p2 の順で直線上p2 ON_SEGMENT = 0, // p0->p2->p1 の順で線分p0p1上にp2 }; ccw_t ccw(Point p0, Point p1, Point p2) { Vector a = p1 - p0, b = p2 - p0; if ( cross(a, b) > EPS ) return COUNTER_CLOCKWISE; if ( cross(a, b) < -EPS ) return CLOCKWISE; if ( dot(a, b) < -EPS ) return ONLINE_BACK; if ( a.norm() < b.norm() ) return ONLINE_FRONT; return ON_SEGMENT; } class Segment { //線分 public: Point p1, p2; Segment(){} Segment(Point p1, Point p2):p1(p1), p2(p2){} }; using Line = Segment; // *** 線分の交差判定 *** bool intersect(const Point& p1, const Point& p2, const Point& p3, const Point& p4) { return ( ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0 ); } bool intersect(const Segment& s1, const Segment& s2) { // 交差していたらtrue return intersect(s1.p1, s1.p2, s2.p1, s2.p2); } //*** 線分の交点 *** Point getCrossPoint(Segment s1, Segment s2) { // 線分の交点が存在するから調べた後つかうこと Vector base = s2.p2 - s2.p1; double d1 = abs(cross(base, s1.p1 - s2.p1)), d2 = abs(cross(base, s1.p2 - s2.p1)); double t = d1 / (d1 + d2); return s1.p1 + (s1.p2 - s1.p1) * t; } // *** 距離 *** double getDistance(Point& a, Point& b) { // 点aと点bの距離 return length(a - b); } double getDistanceLP(Line& l, Point& p) { // 直線sと点pの距離 return length(cross(l.p2 - l.p1, p - l.p1) / length(l.p2 - l.p1)); } double getDistanceSP(Segment s, Point p) { // 線分sと点pの距離 if( dot(s.p2 - s.p1, p - s.p1) < EPS ) return length(p - s.p1); if( dot(s.p1 - s.p2, p - s.p2) < EPS ) return length(p - s.p2); return getDistanceLP(s, p); } double getDistanceSS(Segment s1, Segment s2) { // 線分s1と線分s2の交点 if( intersect(s1, s2) ) return 0.0; //交わっているとき return min(min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2)), min(getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2))); } class Rectangle { // 長方形 public: // 3 2 // 0 1 (反時計回りに長方形の頂点をいれること) vector<Point> p; // 点を順番にいれること Rectangle(vector<Point>&p):p(p) { rep(i, 3) reps(j, i + 1, 4) { //適当な順番にいれても大丈夫なように? int cnt = 0; rep(k, 4) if(k != i && k != j) { cnt += ccw(p[i], p[j], p[k]) == COUNTER_CLOCKWISE; } if(cnt == 2) { swap(p[i + 1], p[j]); break; } } } bool intersect(const Segment& s) { // 線分sと長方形の少なくとも1辺が交差していればtrue bool flag = false; rep(i, 4) flag |= ::intersect(s, Segment(p[i], p[(i + 1) % 4])); return flag; } bool contain(const Point& pp) { // 点ppが長方形内に含まれれば(辺を含まない)true bool flag = true; rep(i, 4) flag &= ccw(p[i], p[(i + 1) % 4], pp) == COUNTER_CLOCKWISE; return flag; } bool contain(const Segment& s) { // 線分sが長方形内に含まれれば(辺を含まない)true return contain(s.p1) && contain(s.p2); } }; const int MAX_N = 210; using TYPE = double; // 距離の型を入れる vector<pair<int, TYPE> > G[MAX_N]; vector<TYPE> dijkstra(int start){ vector<TYPE> dist(MAX_N, INFF); dist[start] = 0;//dist[i] := start->iまでの最短距離 priority_queue<pair<TYPE, int>, vector<pair<TYPE, int> >, greater<pair<TYPE, int> > > que; que.push(make_pair(0, start)); while(!que.empty()){ TYPE cost; int u;//今までにかかった時間 現在の頂点 cost = que.top().first, u = que.top().second; que.pop(); if(dist[u] < cost) continue; for (auto tmp : G[u]){ int v = tmp.first; TYPE time = tmp.second;//隣接する頂点 その頂点まで行く時間 if(dist[v] > dist[u] + time){//u->v dist[v] = dist[u] + time; que.push(make_pair(dist[v], v)); } } } return dist; } int main(void){ while(1) { int N, M, L; scanf("%d %d %d", &N, &M, &L); if(N == 0 && M == 0 && L == 0) break; M--, L--; vector<vector<Segment>> star; rep(i, N){ double x, y, a, r; scanf("%lf %lf %lf %lf", &x, &y, &a, &r); Point c(x, y); vector<Point> v; rep(j, 5) { // double rad = (90 + a + j * 72) * PI / 180.0; // v.pb(Point(x, y) + Point(cos(rad), sin(rad)) * r); v.pb(rotationalTransfer(c, r, 90 + a + j * 72.)); } vector<Segment> seg; // 星の5辺を追加 rep(j, 5) seg.pb(Segment(v[j % 5], v[(j + 2) % 5])); star.pb(seg); } rep(i, MAX_N) G[i].clear(); rep(i, star.size())rep(j, star.size()){ if(i == j) continue; double mi = INF; for(auto u : star[i])for(auto v : star[j]){ chmin(mi, getDistanceSS(u, v)); } // printf("mi %f\n", mi); G[i].pb(mp(j, mi)), G[j].pb(mp(i, mi)); } auto ret = dijkstra(M); printf("%.9f\n", ret[L]); } return 0; }
aoj 1157 Roll-A-Big-Ball
問題概要
ボールが通る線分とそれをじゃまするN個の直方体が与えられる.
直方体に重ならずに線分をスタートからゴールまで通ることができるボールの半径の最大値を求める.
解法
長方形と線分の距離をdとして求める半径をrとすると,
d <= hのとき, r = d
d > hのとき, (d * d + h * h) / (2 * h) (三平方の定理)
と求めることができるので, これをすべての長方形に対して行い, rの最小値を答えとすればいい.
ただし, 線分と長方形が交わっている場合, 答えは0になるので, そのチェックをすること.
ミス
線分と長方形の頂点との距離のみを考えていて,WAした.
線分と長方形の4辺の距離を考えないとだめ. 長方形の頂点が線分と一番近い点とは限らない.
コード
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef vector<int> vint; typedef pair<int,int> pint; typedef vector<pint> vpint; #define rep(i,n) for(int i=0;i<(n);i++) #define REP(i,n) for(int i=n-1;i>=(0);i--) #define reps(i,f,n) for(int i=(f);i<(n);i++) #define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++) #define all(v) (v).begin(),(v).end() #define eall(v) unique(all(v), v.end()) #define pb push_back #define mp make_pair #define fi first #define se second #define chmax(a, b) a = max(a, b); #define chmin(a, b) a = min(a, b); const int MOD = 1e9 + 7; const int INF = 1e9; const ll INFF = 1e18; const double EPS = 1e-10; template<class T> bool eq(T a, T b){ return abs(a - b) < EPS; } class Point { // 点 public: double x, y; Point(double x = 0, double y = 0):x(x), y(y){} Point operator + (Point p) { return Point(x + p.x, y + p.y); } Point operator - (Point p) { return Point(x - p.x, y - p.y); } Point operator * (double a) { return Point(a * x, a * y); } Point operator / (double a) { return Point(x / a, y / a); } double abs() const { return sqrt(norm()); } double norm() const { return x * x + y * y; } // bool operator < (const Point &p) const { return x != p.x ? x < p.x : y < p.y; } bool operator < (const Point &p) const { // 誤差を許容して比較 return x + EPS < p.x || (eq<double>(x, p.x) && y + EPS < p.y); } bool operator == (const Point &p) const { return (eq<double>(x, p.x) && eq<double>(y, p.y)); } }; using Vector = Point; double dot(const Vector& a, const Vector& b) { return a.x * b.x + a.y * b.y; } // ベクトルaとbの内積 double cross(const Vector& a, const Vector& b) { return a.x * b.y - a.y * b.x; } // ベクトルaとbの外積 double length2(const Point& a) { return a.norm(); } // 通常の長さの2乗 double length(const Point& a) { return a.abs(); } // 通常の長さ enum ccw_t { COUNTER_CLOCKWISE = 1, // p0->p1 反時計回りの方向にp2 CLOCKWISE = -1, // p0->p1 時計回りの方向にp2 ONLINE_BACK = 2, // p2->p0->p1 の順で直線上でp2 ONLINE_FRONT = -2, // p0->p1->p2 の順で直線上p2 ON_SEGMENT = 0, // p0->p2->p1 の順で線分p0p1上にp2 }; ccw_t ccw(Point p0, Point p1, Point p2) { Vector a = p1 - p0, b = p2 - p0; if ( cross(a, b) > EPS ) return COUNTER_CLOCKWISE; if ( cross(a, b) < -EPS ) return CLOCKWISE; if ( dot(a, b) < -EPS ) return ONLINE_BACK; if ( a.norm() < b.norm() ) return ONLINE_FRONT; return ON_SEGMENT; } class Segment { //線分 public: Point p1, p2; Segment(){} Segment(Point p1, Point p2):p1(p1), p2(p2){} }; using Line = Segment; // *** 線分の交差判定 *** bool intersect(const Point& p1, const Point& p2, const Point& p3, const Point& p4) { return ( ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0 ); } bool intersect(const Segment& s1, const Segment& s2) { // 交差していたらtrue return intersect(s1.p1, s1.p2, s2.p1, s2.p2); } //*** 線分の交点 *** Point getCrossPoint(Segment s1, Segment s2) { Vector base = s2.p2 - s2.p1; double d1 = abs(cross(base, s1.p1 - s2.p1)), d2 = abs(cross(base, s1.p2 - s2.p1)); double t = d1 / (d1 + d2); return s1.p1 + (s1.p2 - s1.p1) * t; } // *** 距離 *** double getDistance(Point& a, Point& b) { // 点aと点bの距離 return length(a - b); } double getDistanceLP(Line& l, Point& p) { // 直線sと点pの距離 return length(cross(l.p2 - l.p1, p - l.p1) / length(l.p2 - l.p1)); } double getDistanceSP(Segment s, Point p) { // 線分sと点pの距離 if( dot(s.p2 - s.p1, p - s.p1) < EPS ) return length(p - s.p1); if( dot(s.p1 - s.p2, p - s.p2) < EPS ) return length(p - s.p2); return getDistanceLP(s, p); } double getDistanceSS(Segment s1, Segment s2) { if( intersect(s1, s2) ) return 0.0; return min(min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2)), min(getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2))); } class Rectangle { // 長方形 public: // 3 2 // 0 1 (反時計回りに長方形の頂点をいれること) vector<Point> p; // 点を順番にいれること Rectangle(vector<Point>&p):p(p) { rep(i, 3) reps(j, i + 1, 4) { //適当な順番にいれても大丈夫なように? int cnt = 0; rep(k, 4) if(k != i && k != j) { cnt += ccw(p[i], p[j], p[k]) == COUNTER_CLOCKWISE; } if(cnt == 2) { swap(p[i + 1], p[j]); break; } } } bool intersect(const Segment& s) { // 線分sと長方形の少なくとも1辺が交差していればtrue bool flag = false; rep(i, 4) flag |= ::intersect(s, Segment(p[i], p[(i + 1) % 4])); return flag; } bool contain(const Point& pp) { // 点ppが長方形内に含まれれば(辺を含まない)true bool flag = true; rep(i, 4) flag &= ccw(p[i], p[(i + 1) % 4], pp) == COUNTER_CLOCKWISE; return flag; } bool contain(const Segment& s) { // 線分sが長方形内に含まれれば(辺を含まない)true return contain(s.p1) && contain(s.p2); } }; int main(void) { while(1) { int N; scanf("%d", &N); if(N == 0) break; double sx, sy, ex, ey; scanf("%lf %lf %lf %lf", &sx, &sy, &ex, &ey); double minx[55], miny[55], maxx[55], maxy[55], h[55]; rep(i, N) scanf("%lf %lf %lf %lf %lf", &minx[i], &miny[i], &maxx[i], &maxy[i], &h[i]); Segment L(Point(sx, sy), Point(ex, ey)); double ans = INF; rep(i, N) { vector<Point> tmp({Point(minx[i], miny[i]), Point(maxx[i], miny[i]), Point(maxx[i], maxy[i]), Point(minx[i], maxy[i])}); Rectangle rec(tmp); if(rec.intersect(L) || rec.contain(L)) { ans = 0.0; continue; } double d = INF; chmin(d, getDistanceSS(L, Segment(Point(minx[i], miny[i]), Point(minx[i], maxy[i])))); chmin(d, getDistanceSS(L, Segment(Point(minx[i], maxy[i]), Point(maxx[i], maxy[i])))); chmin(d, getDistanceSS(L, Segment(Point(maxx[i], maxy[i]), Point(maxx[i], miny[i])))); chmin(d, getDistanceSS(L, Segment(Point(maxx[i], miny[i]), Point(minx[i], miny[i])))); double r; if(d <= h[i]) r = d; else r = (d * d + h[i] * h[i]) / (2 * h[i]); chmin(ans, r); } printf("%.9f\n", ans); } return 0; }
aoj 2009 Area Separation
問題概要
頂点を(-100,-100)、(100,-100)、(100,100)、(-100,100) とする正方形とn個の線分が与えられる. 線分の両端は正方形の辺上にある. 線分により正方形がいくつに分割されるかを求めよ.
解法
順番に線分を書いていくことを考える. いま書く線分とすでに書かれているすべての線分との交点の集合の要素の数を求めると, いま書く線分によって新たに作られる領域の数は, 交点の数 + 1 となる.
ただし, 正方形の辺上にできる線分どうしの交点は交点に含まない.
Point型には比較関数 < を定義しているので, もとめた交点をsetにいれていけば, 重複をゆるさず交点の数をカウントすることができる.
ミス
Point型に定義している operator < をどうするべきなのかわからない.
const double EPS = 1e-10; template<class T> bool eq(T a, T b){ return abs(a - b) < EPS; } class Point { // 点 public: double x, y; Point(double x = 0, double y = 0):x(x), y(y){} Point operator + (Point p) { return Point(x + p.x, y + p.y); } Point operator - (Point p) { return Point(x - p.x, y - p.y); } Point operator * (double a) { return Point(a * x, a * y); } Point operator / (double a) { return Point(x / a, y / a); } double abs() const { return sqrt(norm()); } double norm() const { return x * x + y * y; } bool operator < (const Point &p) const { return x != p.x ? x < p.x : y < p.y; } bool operator == (const Point &p) const { return (eq<double>(x, p.x) && eq<double>(y, p.y)); } }; int main(void) { set<Point> se1; se1.insert({ Point(1.000000000001, 1.000000000001), Point(1.000000000002, 1.000000000002), Point(1.000000000003, 1.000000000003), Point(1.000000000004, 1.000000000004), Point(1.000000000005, 1.000000000005), Point(1.000000000001, 1.000000000005), Point(1.000000000002, 1.000000000004), }); cout << se1.size() << endl; return 0; }
上のコードの出力は7となってしまう.
const double EPS = 1e-10; template<class T> bool eq(T a, T b){ return abs(a - b) < EPS; } class Point { // 点 public: double x, y; Point(double x = 0, double y = 0):x(x), y(y){} Point operator + (Point p) { return Point(x + p.x, y + p.y); } Point operator - (Point p) { return Point(x - p.x, y - p.y); } Point operator * (double a) { return Point(a * x, a * y); } Point operator / (double a) { return Point(x / a, y / a); } double abs() const { return sqrt(norm()); } double norm() const { return x * x + y * y; } bool operator < (const Point &p) const { // 誤差を許容して比較 return x + EPS < p.x || (eq<double>(x, p.x) && y + EPS < p.y); } bool operator == (const Point &p) const { return (eq<double>(x, p.x) && eq<double>(y, p.y)); } }; int main(void) { set<Point> se1; se1.insert({ Point(1.000000000001, 1.000000000001), Point(1.000000000002, 1.000000000002), Point(1.000000000003, 1.000000000003), Point(1.000000000004, 1.000000000004), Point(1.000000000005, 1.000000000005), Point(1.000000000001, 1.000000000005), Point(1.000000000002, 1.000000000004), }); cout << se1.size() << endl; return 0; }
上のコードの出力は1となる.
今回の問題では, どちらのoperator < でもACできたが, 上のコードでは誤差を考えた比較関数になっていないので, ケースによってはだめかもしれない?
operator < はどうすべきなんだ?
コード
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef vector<int> vint; typedef pair<int,int> pint; typedef vector<pint> vpint; #define rep(i,n) for(int i=0;i<(n);i++) #define REP(i,n) for(int i=n-1;i>=(0);i--) #define reps(i,f,n) for(int i=(f);i<(n);i++) #define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++) #define all(v) (v).begin(),(v).end() #define eall(v) unique(all(v), v.end()) #define pb push_back #define mp make_pair #define fi first #define se second #define chmax(a, b) a = max(a, b); #define chmin(a, b) a = min(a, b); const int MOD = 1e9 + 7; const int INF = 1e9; const ll INFF = 1e18; const double EPS = 1e-10; template<class T> bool eq(T a, T b){ return abs(a - b) < EPS; } class Point { // 点 public: double x, y; Point(double x = 0, double y = 0):x(x), y(y){} Point operator + (Point p) { return Point(x + p.x, y + p.y); } Point operator - (Point p) { return Point(x - p.x, y - p.y); } Point operator * (double a) { return Point(a * x, a * y); } Point operator / (double a) { return Point(x / a, y / a); } double abs() const { return sqrt(norm()); } double norm() const { return x * x + y * y; } // bool operator < (const Point &p) const { return x != p.x ? x < p.x : y < p.y; } bool operator < (const Point &p) const { // 誤差を許容して比較 return x + EPS < p.x || (eq<double>(x, p.x) && y + EPS < p.y); } bool operator == (const Point &p) const { return (eq<double>(x, p.x) && eq<double>(y, p.y)); } }; using Vector = Point; double dot(const Vector& a, const Vector& b) { return a.x * b.x + a.y * b.y; } // ベクトルaとbの内積 double cross(const Vector& a, const Vector& b) { return a.x * b.y - a.y * b.x; } // ベクトルaとbの外積 double length2(const Point& a) { return a.norm(); } // 通常の長さの2乗 double length(const Point& a) { return a.abs(); } // 通常の長さ enum ccw_t { COUNTER_CLOCKWISE = 1, // p0->p1 反時計回りの方向にp2 CLOCKWISE = -1, // p0->p1 時計回りの方向にp2 ONLINE_BACK = 2, // p2->p0->p1 の順で直線上でp2 ONLINE_FRONT = -2, // p0->p1->p2 の順で直線上p2 ON_SEGMENT = 0, // p0->p2->p1 の順で線分p0p1上にp2 }; ccw_t ccw(Point p0, Point p1, Point p2) { Vector a = p1 - p0, b = p2 - p0; if ( cross(a, b) > EPS ) return COUNTER_CLOCKWISE; if ( cross(a, b) < -EPS ) return CLOCKWISE; if ( dot(a, b) < -EPS ) return ONLINE_BACK; if ( a.norm() < b.norm() ) return ONLINE_FRONT; return ON_SEGMENT; } class Segment { //線分 public: Point p1, p2; Segment(){} Segment(Point p1, Point p2):p1(p1), p2(p2){} }; using Line = Segment; // *** 線分の交差判定 *** bool intersect(const Point& p1, const Point& p2, const Point& p3, const Point& p4) { return ( ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0 ); } bool intersect(const Segment& s1, const Segment& s2) { // 交差していたらtrue return intersect(s1.p1, s1.p2, s2.p1, s2.p2); } //*** 線分の交点 *** Point getCrossPoint(Segment s1, Segment s2) { Vector base = s2.p2 - s2.p1; double d1 = abs(cross(base, s1.p1 - s2.p1)), d2 = abs(cross(base, s1.p2 - s2.p1)); double t = d1 / (d1 + d2); return s1.p1 + (s1.p2 - s1.p1) * t; } // *** 距離 *** double getDistance(Point& a, Point& b) { // 点aと点bの距離 return length(a - b); } double getDistanceLP(Line& l, Point& p) { // 直線sと点pの距離 return length(cross(l.p2 - l.p1, p - l.p1) / length(l.p2 - l.p1)); } double getDistanceSP(Segment s, Point p) { // 線分sと点pの距離 if( dot(s.p2 - s.p1, p - s.p1) < EPS ) return length(p - s.p1); if( dot(s.p1 - s.p2, p - s.p2) < EPS ) return length(p - s.p2); return getDistanceLP(s, p); } double getDistanceSS(Segment s1, Segment s2) { if( intersect(s1, s2) ) return 0.0; return min(min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2)), min(getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2))); } class Rectangle { // 長方形 public: // 3 2 // 0 1 (反時計回りに長方形の頂点をいれること) vector<Point> p; // 点を順番にいれること Rectangle(vector<Point>&p):p(p) { rep(i, 3) reps(j, i + 1, 4) { //適当な順番にいれても大丈夫なように? int cnt = 0; rep(k, 4) if(k != i && k != j) { cnt += ccw(p[i], p[j], p[k]) == COUNTER_CLOCKWISE; } if(cnt == 2) { swap(p[i + 1], p[j]); break; } } } bool intersect(const Segment& s) { // 線分sと長方形の少なくとも1辺が交差していればtrue bool flag = false; rep(i, 4) flag |= ::intersect(s, Segment(p[i], p[(i + 1) % 4])); return flag; } bool contain(const Point& pp) { // 点ppが長方形内に含まれれば(辺を含まない)true bool flag = true; rep(i, 4) flag &= ccw(p[i], p[(i + 1) % 4], pp) == COUNTER_CLOCKWISE; return flag; } bool contain(const Segment& s) { // 線分sが長方形内に含まれれば(辺を含まない)true return contain(s.p1) && contain(s.p2); } }; int main(void) { while(1) { int n; scanf("%d", &n); if(n == 0) break; double x1[110], y1[110], x2[110], y2[110]; rep(i, n) scanf("%lf %lf %lf %lf", &x1[i], &y1[i], &x2[i], &y2[i]); if(n == 1){ printf("2\n"); break; } int ans = 2; reps(i, 1, n){ set<Point> se; rep(j, i){ Segment u(Point(x1[i], y1[i]), Point(x2[i], y2[i])), v(Point(x1[j], y1[j]), Point(x2[j], y2[j])); if(!intersect(u, v)) continue; se.insert(getCrossPoint(u, v)); } // 長方形の円周上の点を除く for(auto t : se) { if(eq<double>(t.x, -100.0) || eq<double>(t.x, 100.0) || eq<double>(t.y, -100.0) || eq<double>(t.y, 100.0)){ se.erase(t); } } ans += (int)se.size() + 1; } printf("%d\n", ans); } return 0; }