Chapter 5. Graph Theory:: Fundamentals:: Intermediate

10457 - Magic Carphp

題意一開始看起來有點費解,其實最後就是要起點到終點的路徑上最大邊與最小邊之差越小越好。這樣咱們能夠先將邊排個序,而後枚舉路徑上的最小邊,以後依次將比它大的邊按升序的順序添加,直到起點和重點連通爲止,這樣就獲得了一個可行解並嘗試去更新記錄的最優解。等全部可能的最小邊都枚舉完以後就能夠獲得最終的最優解了。node

#include<stdio.h> #include<string.h> #include<algorithm>
#define MAXN 210
#define MAXM 1010
#define INF 0x7fffffff
int N, M, SE, EE, p[MAXN]; struct Edge { int u, v, w; bool operator < (const Edge &t) const { return w < t.w; } }e[MAXM]; int find(int x) { return p[x] == x ? x : (p[x] = find(p[x])); } void merge(int x, int y) { int tx = find(x), ty = find(y); if(tx != ty) p[tx] = ty; } void input() { for(int i = 0; i < M; i ++) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w); scanf("%d%d", &SE, &EE); } void process() { std::sort(e, e + M); int k; scanf("%d", &k); while(k --) { int s, t; scanf("%d%d", &s, &t); int ans = INF; for(int i = 0; i < M; i ++) { int j; for(int j = 1; j <= N; j ++) p[j] = j; for(j = i; j < M; j ++) { merge(e[j].u, e[j].v); if(find(s) == find(t)) break; } if(j != M) ans = std::min(ans, e[j].w - e[i].w); } printf("%d\n", ans + SE + EE); } } int main() { while(scanf("%d%d", &N, &M) == 2) { input(); process(); } return 0; }
View Code

 

10607 - Siegeios

又是一個神同樣的題意,我再次沒讀懂,不過根據種種跡象來揣測的話,應該是能夠先佔領邊疆城市,而後就能夠佔領和已佔領的城市相鄰的城市了,直到可以圍住首都。這個題目裏面有個比較特殊的條件,就是「There is no point in Flatland, which has a boundary with more than three other provinces i.e. four squares, which have the same vertex can’t belong to four different provinces.」,實際上這個條件保證了包圍A的全部城市是連通的。既然包圍A的城市是連通的,那麼只要考慮從邊疆開始佔領到任意一個包圍A的城市就能夠了,最後在加上包圍A的城市的數量就好了。因而咱們能夠把全部包圍A的城市當作一個起點S,將外界當作終點T,而後相鄰的兩個城市連邊,最後求S到T的最短路就能夠了。算法

這個題目的坑仍是不少的,首先就是何時輸出-1,我想來想去,最後仍是在UVA的論壇上找到了一種狀況:dom

5 5ide

CCCCC優化

CAAACthis

CABACspa

CAAAC3d

CCCCC

這種狀況就應當輸出-1了,由於根據題意B應該是和A相鄰的,可是咱們沒辦法經過給定的規則佔領B,因此輸出-1。

其次,題目的數據裏面是有空格的,還有一些>127的ASC碼,這些比較特殊的字符也全當正常的城市去處理就行了。

#include<stdio.h> #include<string.h>
int N, M, g[260][260], first[260], e, next[1000010], v[1000010], cnt, p[260], q[40010], dis[260]; char b[210][210]; bool adj[260]; const int S = 0, T = 256; const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1}; int find(int x) { return p[x] == x ? x : (p[x] = find(p[x])); } void merge(int x, int y) { int tx = find(x), ty = find(y); if(tx != ty) p[tx] = ty; } bool inside(int x, int y) { return x >= 0 && x < N && y >= 0 && y < M; } void add(int x, int y) { v[e] = y; next[e] = first[x], first[x] = e ++; } void process() { cnt = 0; memset(adj, 0, sizeof(adj)); for(int i = 0; i < N; i ++) for(int j = 0; j < M; j ++) if(b[i][j] == 'A') { for(int k = 0; k < 4; k ++) { int ni = i + dx[k], nj = j + dy[k]; if(!inside(ni, nj)) continue; int c = (int)b[ni][nj] & 255; if(c != 'A' && !adj[c]) ++ cnt, adj[c] = true; } } memset(g, 0, sizeof(g)); memset(first, -1, sizeof(first)), e = 0; for(int i = 0; i < 256; i ++) p[i] = i; for(int i = 0; i < N; i ++) for(int j = 0; j < M; j ++) { int c = (int)b[i][j] & 255; if(c == 'A' || c <= ' ') continue; for(int k = 0; k < 4; k ++) { int ni = i + dx[k], nj = j + dy[k], nc; if(!inside(ni, nj)) nc = T; else nc = (int)b[ni][nj] & 255; if(nc == 'A' || c == nc) continue; if(adj[c] && adj[nc]) { merge(c, nc); continue; } if(adj[c]) c = S; else if(adj[nc]) nc = S; if(!g[c][nc]) { g[c][nc] = 1; add(c, nc), add(nc, c); } } } int node = -1; for(int i = 0; i < 256; i ++) if(adj[i]) { if(node == -1) node = find(i); else if(node != find(i)) { printf("-1\n"); return ; } } int rear = 0; memset(dis, -1, sizeof(dis)); dis[S] = 0; q[rear ++] = S; for(int i = 0; i < rear; i ++) for(int j = first[q[i]]; j != -1; j = next[j]) if(dis[v[j]] == -1) dis[v[j]] = dis[q[i]] + 1, q[rear ++] = v[j]; printf("%d\n", dis[T] > 0 ? dis[T] - 1 + cnt  : -1); } int main() { char s[20]; for(;;) { gets(s); sscanf(s, "%d%d", &N, &M); if(N == 0 && M == 0) break; for(int i = 0; i < N; i ++) gets(b[i]); process(); } return 0; }
View Code

 

10798 - Be wary of Roses

因爲要綜合考慮4種狀況,那麼每走一步,咱們能夠看做4種狀況各走了一步,並且其餘3個位置是能夠根據當前位置計算出來,這樣每一步咱們均可以知道哪一種狀況會多踩一朵花。爲了可以完整的記錄下此時此刻的狀態,咱們能夠將4種狀況各自通過的花的數量記錄到狀態中,同時將當前所在的位置記錄到狀態中。以後咱們只要留意哪些狀態能夠走出去並選出其中最優的狀態便可。這樣咱們實際上只須要關注哪些狀態可達就能夠了,因而用BFS就能夠搞定了。

因爲一開始交上去TLE了,因此適當地作了一些常數優化,好比用手寫隊列替代STL的隊列等等。

#include<stdio.h> #include<string.h> #include<algorithm>
#define MAXN 25
#define INF 0x3f3f3f3f
int N, a[MAXN][MAXN], vis[MAXN][MAXN][15010], R; const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1}, fac[] = {1, 11, 121, 1331}; int getMax(int a[]) { int ans = a[0]; for(int i = 1; i < 4; i ++) ans = std::max(ans, a[i]); return ans; } struct St { int x, y, a[4], max, code; St(){} St(int _x, int _y) : x(_x), y(_y) { code = max = 0; memset(a, 0, sizeof(a)); } }q[7000010]; void input() { char s[MAXN]; for(int i = 0; i < N; i ++) { scanf("%s", s); for(int j = 0; j < N; j ++) a[i][j] = s[j] == 'R'; } } bool inside(int x, int y) { return x >= 0 && x < N && y >= 0 && y < N; } void process() { const int ox = N >> 1, oy = N >> 1, most = N >> 1; int front = 0, rear = 0; vis[ox][oy][0] = R, q[rear ++] = St(ox, oy); int ans = INF; while(front < rear) { St st = q[front ++]; if(st.max >= ans) continue; for(int i = 0; i < 4; i ++) { int x = st.x + dx[i], y = st.y + dy[i]; if(!inside(x, y)) { ans = std::min(ans, st.max); continue; } int bx = x - ox, by = y - oy; St nst = st; nst.x = x, nst.y = y; if(a[bx + ox][by + oy]) ++ nst.a[0], nst.code += fac[0]; if(a[-bx + ox][-by + oy]) ++ nst.a[1], nst.code += fac[1]; if(a[-by + ox][bx + oy]) ++ nst.a[2], nst.code += fac[2]; if(a[by + ox][-bx + oy]) ++ nst.a[3], nst.code += fac[3]; nst.max = getMax(nst.a); if(nst.max > most) continue; if(vis[x][y][nst.code] != R) vis[x][y][nst.code] = R, q[rear ++] = nst; } } printf("At most %d rose(s) trampled.\n", ans); } int main() { R = 0; while(scanf("%d", &N), N) { ++ R; input(); process(); } return 0; }
View Code

 

10941 - Words adjustment

這個題目能夠等價成一開始有一個字符串s,而後選出另外一個字符串t作減法,並將剩餘的字符串看作新的s,如此反覆進行,直到s的長度爲0爲止。這樣作的複雜度主要取決於作減法的時候一共能拓展出多少個新的字符串,這個值的上限我沒有仔細算,不過至少這樣作能夠AC這個題目。

#include<stdio.h> #include<string.h> #include<iostream> #include<set> #include<queue>
#define MAXN 1010
class State { public: int d; std::string s; State() {} State(int d, std::string s) { this->d = d, this->s = s; } }q[1000010]; const int Q = 1000010; int N; std::string x, y, s[MAXN]; void input() { std::cin >> x >> y; std::cin >> N; for(int i = 0; i < N; i ++) std::cin >> s[i]; } void process() { int front, rear; front = rear = 0; if(x.size() < y.size()) std::swap(x, y); if(x == y) { std::cout << "0\n"; return ; } int n = y.size(); if(x.substr(0, n) != y) { std::cout << "-1\n"; return ; } std::set<std::string> set; std::string a =  x.substr(n, x.size() - n); q[rear ++] = State(1, a), set.insert(a); while(front != rear) { State st = q[front ++]; if(front >= Q) front = 0; n = st.s.size(); for(int i = 0; i < N; i ++) { if(st.s == s[i]) { std::cout << st.d << "\n"; return ; } std::string a; bool over = false; int nn = s[i].size(); if(n <= nn) { if(s[i].substr(0, n) != st.s) over = true; else a = s[i].substr(n, nn - n); } else { if(st.s.substr(0, nn) != s[i]) over = true; else a = st.s.substr(nn, n - nn); } if(over) continue; if(!set.count(a)) { set.insert(a); q[rear ++] = State(st.d + 1, a); if(rear >= Q) rear = 0; } } } std::cout << "-1\n"; } int main() { int t; std::cin >> t; while(t --) { input(); process(); } return 0; }
View Code

 

10968 - KuPellaKeS

題目中有說「至多兩個節點」有奇數個鄰居,那麼天然就要分三種狀況討論。

在討論以前咱們先排除一種特殊狀況,就是存在孤立點,這樣的狀況必定無解。

若是沒有節點有奇數個鄰居,那麼直接輸出0就能夠了。

若是隻有一個點有奇數個鄰居,哦,其實不可能有這種狀況。

若是有兩個點有奇數個鄰居,這種狀況就是比較通常的狀況了,咱們不妨設兩個點分別爲s和t。

這時咱們必須刪掉和s相鄰的一條邊使得s變成一個有偶數個鄰居的點,那麼若是s和t之間有邊,天然刪掉這條邊以後就OK了,但若是s和t沒有邊的話,這時又會製造一個新的有奇數個鄰居的節點,因而又要重複上面的步驟。這樣不難發現,最後刪掉的全部邊實際上組成了一條路徑,而咱們就是要這條路徑越短越好,想到這天然就想到用最短路相關算法去求解了。不過仍是有些細節須要注意,首先,若是s或t自己只有一個鄰居,那麼是無解的,這種狀況應當特判掉,其次,咱們刪掉的這條路徑不能通過只有兩個鄰居的節點,不然刪掉這條路徑後就會出現孤立點了,因而咱們應當先將圖中只有兩個鄰居的節點忽視掉再作最短路。

#include<stdio.h> #include<string.h>
#define MAXN 1010
#define MAXM 2000010
#define INF 0x3f3f3f3f
int N, M, dgr[MAXN], first[MAXN], e, next[MAXM], v[MAXM], q[MAXN], dis[MAXN]; bool del[MAXN]; void add(int x, int y) { v[e] = y; next[e] = first[x], first[x] = e ++; } void process() { int s = -1, t = -1; memset(del, 0, sizeof(del[0]) * (N + 1)); for(int i = 1; i <= N; i ++) { if(dgr[i] <= 1) { printf("Poor Koorosh\n"); return ; } if(dgr[i] == 2) del[i] = true; else if(dgr[i] & 1) { if(s == -1) s = i; else t = i; } } if(s == -1) { printf("0\n"); return ; } memset(dis, 0x3f, sizeof(dis[0]) * (N + 1)); int rear = 0; dis[s] = 0, q[rear ++] = s; for(int i = 0; i < rear; i ++) for(int j = first[q[i]]; j != -1; j = next[j]) if(!del[v[j]] && dis[q[i]] + 1 < dis[v[j]]) dis[v[j]] = dis[q[i]] + 1, q[rear ++] = v[j]; if(dis[t] != INF) printf("%d\n", dis[t]); else printf("Poor Koorosh\n"); } int main() { while(scanf("%d%d", &N, &M), N) { memset(dgr, 0, sizeof(dgr[0]) * (N + 1)); memset(first, -1, sizeof(first[0]) * (N + 1)), e = 0; for(int i = 0; i < M; i ++) { int x, y; scanf("%d%d", &x, &y); add(x, y), add(y, x); ++ dgr[x], ++ dgr[y]; } process(); } return 0; }
View Code

 

11165 - Galactic Travel

這個題目最基本的思路就是用BFS了,可是因爲點的數量巨大,直接去BFS確定會超時,可是咱們仔細分析一下不難發現之因此會超時,是由於對於每一個點咱們都會嘗試去掃描一遍全部與之相鄰的點,可是若是每次咱們都能只掃描那些尚未被訪問的點的話天然就不會超時了。因而咱們須要一種策略使得每次能快速地找到和當前的點相鄰的點中還有哪些點是沒有被訪問的,這樣的策略有不少種,好比並查集,線段樹等等,我在這裏使用了並查集來達到這個效果。

並查集中p[x]指向的是在標號大於等於x的節點中,最小的未訪問的節點。

#include<stdio.h> #include<string.h> #include<ctype.h> #include<algorithm>
#define MAXN 100010
#define MAXK 41010
struct Seg { int u, v[2]; bool operator < (const Seg &t) const { if(u != t.u) return u < t.u; if(v[0] != t.v[0]) return v[0] < t.v[0]; return v[1] < t.v[1]; } }fb[MAXK], seg[MAXN + MAXK]; int N, K, S, T, p[MAXN], dis[MAXN], first[MAXN], e, next[MAXN + MAXK], q[MAXN]; bool cal[MAXN]; void add(int u, Seg &s) { seg[e] = s; next[e] = first[u], first[u] = e ++; } int stack[MAXN]; /** int find(int x) { return p[x] == x ? x : (p[x] = find(p[x])); } Because my OS is Windows, I choose the function below instead of it to avoid stack overflow. */
int find(int x) { int top = 0; while(p[x] != x) stack[top ++] = x, x = p[x]; while(top) p[stack[-- top]] = x; return x; } void merge(int x, int y) { int tx = find(x), ty = find(y); if(tx != ty) p[tx] = ty; } void input() { scanf("%d%d", &N, &K); for(int i = 0; i < K; i ++) { scanf("%d%d-%d", &fb[i].u, &fb[i].v[0], &fb[i].v[1]); if(fb[i].v[0] > fb[i].v[1]) std::swap(fb[i].v[0], fb[i].v[1]); } scanf("%d%d", &S, &T); } void initSeg() { std::sort(fb, fb + K); memset(first, -1, sizeof(first[0]) * N), e = 0; memset(cal, 0, sizeof(cal[0]) * N); int last = -1, u; Seg s; for(int i = 0; i < K; i ++) { u = fb[i].u, cal[u] = true; if(fb[i].v[0] > last + 1) { s.v[0] = last + 1, s.v[1] = fb[i].v[0] - 1; add(u, s); } last = std::max(last, fb[i].v[1]); if(i == K - 1 || fb[i + 1].u != fb[i].u) { if(last + 1 <= N - 1) { s.v[0] = last + 1, s.v[1] = N - 1; add(u, s); } last = -1; } } for(int i = 0; i < N; i ++) if(!cal[i]) { s.v[0] = 0, s.v[1] = N - 1; add(i, s); } } void process() { initSeg(); dis[T] = -1; for(int i = 0; i <= N; i ++) p[i] = i; int rear = 0; dis[S] = 0, q[rear ++] = S; merge(S, S + 1); for(int i = 0; i < rear; i ++) for(int j = first[q[i]]; j != -1; j = next[j]) { int x = seg[j].v[0], y = seg[j].v[1]; for(int u = find(x); u <= y; u = find(u)) { dis[u] = dis[q[i]] + 1, q[rear ++] = u; merge(u, u + 1); } } if(dis[T] == -1) printf("Impossible\n"); else printf("%d\n", dis[T]); } int main() { int t; scanf("%d", &t); for(int tt = 1; tt <= t; tt ++) { input(); printf("Case #%d: ", tt); process(); } return 0; }
View Code

 

11853 - Paintball

首先考慮無解的狀況,那麼必然是從上到下若干個圓連在了一塊兒把路截斷了。那麼咱們就能夠將圓轉化成點,並虛擬一個起點和終點,凡是和上邊界相交的圓都和起點相連,和下邊界相交的圓都和終點相連,而且相交的圓之間也連起來,這樣若是從起點出發能夠到達終點,那麼途徑的圓就會截斷咱們的通路。

解決完無解的狀況後,咱們能夠用相似的思想求解從哪裏能夠進去以及從哪裏能夠出去。

不妨先考慮從哪裏能夠進去,可是這個不大好考慮,因而轉而去考慮哪裏不能夠進去。對於任意一個和左邊界相交的圓來說,若是從這個圓出發,按以前建好的圖走能走到上邊界的話,那麼這個圓的弦,以及弦上面的部分都是不能夠進去的。同理,若是從這個圓出發,能走到下邊界的話,那麼這個圓的弦以及弦下面的部分都是不能夠進去的。最後若是從這個圓出發既不能走到上邊界,也不能走到下邊界,那麼就只有這個圓的弦這部分是不能夠進去的。這樣我就可以處理出來若干個不能進去的位置的區間,根據這些區間就不難找到可能的最下面的能夠進去的位置了。

考慮從哪裏能夠出去是相似的,就再也不贅述了。

#include<stdio.h> #include<string.h> #include<math.h> #include<algorithm>
#define MAXN 1010
#define MAXM 2000100
const double eps = 1e-8; const double bound = 1000.0; int N, first[MAXN], e, next[MAXM], v[MAXM], S[2], SN; int dcmp(double x, double y) { double t = x - y; return (t > eps) - (t < -eps); } double sqr(double x) { return x * x; } struct C { double x, y, r; bool insect(C &t) { return dcmp(sqr(x - t.x) + sqr(y - t.y), sqr(r + t.r)) <= 0; } }c[MAXN]; struct Seg { double x, y; bool operator < (const Seg &t) const { return x > t.x; } }seg[MAXN]; void add(int x, int y) { v[e] = y; next[e] = first[x], first[x] = e ++; } void initGraph() { memset(first, -1, sizeof(first[0]) * (N + 2)), e = 0; for(int i = 0; i < 4; i ++) S[i] = N + i; for(int i = 0; i < N; i ++) { if(dcmp(c[i].y - c[i].r, 0) <= 0) add(i, S[0]), add(S[0], i); if(dcmp(c[i].y + c[i].r, bound) >= 0) add(i, S[1]), add(S[1], i); for(int j = 0; j < N; j ++) if(i != j && c[i].insect(c[j])) add(i, j), add(j, i); } } int d[MAXN], q[MAXN]; void bfs(int s) { memset(d, 0, sizeof(d[0]) * (N + 2)); int rear = 0; d[s] = 1, q[rear ++] = s; for(int i = 0; i < rear; i ++) for(int j = first[q[i]]; j != -1; j = next[j]) if(!d[v[j]]) d[v[j]] = d[q[i]] + 1, q[rear ++] = v[j]; } double find() { std::sort(seg, seg + SN); double pre = bound; for(int i = 0; i < SN; i ++) { if(dcmp(seg[i].y, pre) < 0) return pre; pre = std::min(pre, seg[i].x); } return pre; } void process() { initGraph(); bfs(S[0]); if(d[S[1]]) { printf("IMPOSSIBLE\n"); return ; } double ans[2]; SN = 0; for(int i = 0; i < N; i ++) if(dcmp(c[i].x - c[i].r, 0) < 0) { double t = sqrt(sqr(c[i].r) - sqr(c[i].x)); bfs(i); if(d[S[0]]) seg[SN].x = 0, seg[SN].y = c[i].y + t; else if(d[S[1]]) seg[SN].x = c[i].y - t, seg[SN].y = bound; else seg[SN].x = c[i].y - t, seg[SN].y = c[i].y + t; ++ SN; } ans[0] = find(); SN = 0; for(int i = 0; i < N; i ++) if(dcmp(c[i].x + c[i].r, bound) > 0) { double t = sqrt(sqr(c[i].r) - sqr(bound - c[i].x)); bfs(i); if(d[S[0]]) seg[SN].x = 0, seg[SN].y = c[i].y + t; else if(d[S[1]]) seg[SN].x = c[i].y - t, seg[SN].y = bound; else seg[SN].x = c[i].y - t, seg[SN].y = c[i].y + t; ++ SN; } ans[1] = find(); printf("0.00 %.2f 1000.00 %.2f\n", ans[0], ans[1]); } int main() { while(scanf("%d", &N) == 1) { for(int i = 0; i < N; i ++) scanf("%lf%lf%lf", &c[i].x, &c[i].y, &c[i].r); process(); } return 0; }
View Code

 

1568 - Domino Puzzle

domino骨牌連成的是一條鏈,若是咱們把每一個點數當作一個點的話,那麼這條鏈就至關於一個只有6個頂點的圖上的歐拉道路,再聯想歐拉道路的條件,圖連通而且至多有2個奇度數的點,那麼咱們的目標就至關於用最小的代價構造出一個這樣的圖出來。

若是圖原本就連通的話,那麼咱們只須要在奇度數的點之間連邊,使得最後剩餘的奇度數的點不超過2個就OK了,並且連的時候先挑標號小的點去連。

若是圖原本不連通的話,那麼咱們就要先使其變成連通圖,再像上面那樣考慮就能夠了,可是咱們究竟要連哪些點才能使最終的代價最小呢?若是某個連通塊原本就沒有奇度數的點怎麼辦呢?咱們先解決第二個問題,挑1個標號最小的點並將其當作2個奇度數的點就OK了。在解決了第二個問題以後,咱們就只須要在這些奇數的點之間連邊就可使得圖連通而且最多不超過2個奇度數點了。可是這樣作就必定可以達到最優的狀況嗎?固然不能,由於這樣想連樣例都過不了。。。

接下來就分析一下緣由。按前面的作法,實際上就是刪掉兩個標號最大的奇度數點,剩下的奇度數點的標號之和就是最小代價了,若是這兩個標號最大的奇度數點不是一個連通塊的,那麼天然沒什麼問題,但若是這兩個標號最大的奇度數點是一個連通塊的,而且這個連通塊只有這兩個奇度數點,那該怎麼辦呢?那麼就還要像前面那樣挑一個標號最小的點並將其當作2個奇度數點。因爲多了這2個新的奇度數點,刪掉這兩個標號最大的奇度數點就不必定是最優的狀況了。所以在求解的過程當中咱們分狀況討論一下究竟刪掉哪2個點。

至此,關鍵的問題就分析完了,至於如何打印結果就再也不贅述了。前面的思路中也並無證實這樣作爲何是最優的,不過真正證實起來也並不困難,因此這裏也再也不贅述了。

#include<stdio.h> #include<string.h> #include<algorithm>
int p[10], d[10], q[10][10], qn[10], qm[10]; bool vis[10]; int find(int x) { return p[x] == x ? x : (p[x] = find(p[x])); } void merge(int x, int y) { int tx = find(x), ty = find(y); if(tx != ty) p[tx] = ty; } void makeChoice(int m, int &sv, int &sn) { int min = sv + 1, k; for(int i = 0; i < m; i ++) { int t; if(qn[i] == 2) t = sv - q[i][0] - q[i][1] + 2 * qm[i]; else t = sv - q[i][qn[i] - 1] - q[i][qn[i] - 2]; if(t < min) min = t, k = i; } int max1 = -1, max2 = -1, k1, k2; for(int i = 0; i < m; i ++) if(q[i][qn[i] - 1] > max1) max1 = q[i][qn[i] - 1], k1 = i; for(int i = 0; i < m; i ++) if(i != k1 && q[i][qn[i] - 1] > max2) max2 = q[i][qn[i] - 1], k2 = i; if(sv - max1 - max2 < min) -- qn[k1], -- qn[k2], sv -= max1 + max2, sn -= 2; else { if(qn[k] == 2) q[k][0] = q[k][1] = qm[k], sv = min; else qn[k] -= 2,  sv = min, sn -= 2; } } int main() { int n; while(scanf("%d", &n) == 1) { memset(d, 0, sizeof(d)); for(int i = 1; i <= 6; i ++) p[i] = i; while(n --) { int x, y; scanf("%d%d", &x, &y); merge(x, y); ++ d[x], ++ d[y]; } memset(vis, 0, sizeof(vis)); int m = 0, sn = 0, sv = 0; for(int i = 1; i <= 6; i ++) if(d[i] > 0 && !vis[find(i)]) { vis[find(i)] = true; qn[m] = 0, qm[m] = i; for(int j = i; j <= 6; j ++) if(find(i) == find(j)) if(d[j] & 1) q[m][qn[m] ++] = j, sv += j; sn += qn[m]; std::sort(q[m], q[m] + qn[m]); ++ m; } if(m == 1) { if(sn >= 2) sv -= q[0][sn - 1] + q[0][sn - 2], sn -= 2; printf("%d\n", sv); printf("%d\n", sn / 2); for(int i = 0; i < sn; i += 2) printf("%d %d\n", q[0][i], q[0][i + 1]); continue; } for(int i = 0; i < m; i ++) if(qn[i] == 0) q[i][0] = q[i][1] = qm[i], qn[i] = 2, sn += 2, sv += qm[i] * 2; makeChoice(m, sv, sn); printf("%d\n%d\n", sv, sn / 2); int a[10], an = 0, o1, o2; for(o1 = 0; o1 < m && (qn[o1] & 1) == 0; o1 ++); for(o2 = o1 + 1; o2 < m && (qn[o2] & 1) == 0; o2 ++); if(o1 < m) a[an ++] = o1; for(int i = 0; i < m; i ++) if((qn[i] & 1) == 0) a[an ++] = i; if(o2 < m) a[an ++] = o2; for(int i = 0; i < m - 1; i ++) { int x = a[i], y = a[i + 1]; printf("%d %d\n", q[x][-- qn[x]], q[y][-- qn[y]]); } an = 0; for(int i = 0; i < m; i ++) for(int j = 0; j < qn[i]; j ++) a[an ++] = q[i][j]; for(int i = 0; i < an; i += 2) printf("%d %d\n", a[i], a[i + 1]); } return 0; }
View Code

 

1569 - Multiple

爲了保證是N的最小正整數倍,那麼首先就要保證位數最少,其次要保證最高位儘量小。而保證位數最少是能夠用廣搜作出來的,用d[x]表示當前構造的數爲模N爲x的時候最少須要幾位數字,那麼依次嘗試在x後面添加一位合法的數字y,而後嘗試用d[x]+1去更新d[(x*10+y)%N],這樣看最後d[0]的值就能夠知道是否有解以及最少是幾位數了。至於怎樣保證打印的結果中最高位儘量小就再也不贅述了。

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define INF 0x3f3f3f3f
int N, M, a[20], d[5010], r[5010], q[5010];
bool can[5010];
void print(int x)
{
    if(x == 0) return ;
    for(int i = 0; i < M; i ++)
    {
        int s = (x * 10 + a[i]) % N;
        if(can[s] && d[s] == d[x] + 1)
        {
            printf("%d", a[i]);
            print(s);
            break;
        }
    }
}
int main()
{
    while(scanf("%d%d", &N, &M) == 2)
    {
        for(int i = 0; i < M; i ++) scanf("%d", &a[i]);
        if(N == 0)
        {
            printf("0\n");
            continue;
        }
        std::sort(a, a + M);
        memset(d, 0x3f, sizeof(d[0]) * N);
        std::vector<int> v[5010];
        int rear = 0;
        for(int i = 0; i < M; i ++)
        {
            if(a[i] == 0) continue;
            int x = a[i] % N;
            if(1 < d[x]) d[x] = 1, q[rear ++] = x;
        }
        for(int i = 0; i < rear; i ++)
        {
            int x = q[i];
            for(int j = 0; j < M; j ++)
            {
                int s = (x * 10 + a[j]) % N;
                if(d[x] + 1 < d[s]) d[s] = d[x] + 1, q[rear ++] = s, v[s].push_back(x);
                else if(d[x] + 1 == d[s]) v[s].push_back(x);

            }
        }
        if(d[0] == INF) printf("0");
        else
        {
            memset(can, 0, sizeof(can[0]) * N);
            rear = 0;
            can[0] = true, q[rear ++] = 0;
            for(int i = 0; i < rear; i ++)
            {
                int x = q[i];
                for(int j = 0; j < v[x].size(); j ++)
                {
                    int y = v[x][j];
                    if(!can[y]) can[y] = true, q[rear ++] = y;
                }
            }
            for(int i = 0; i < M; i ++)
            {
                if(a[i] == 0) continue;
                int x = a[i] % N;
                if(can[x])
                {
                    printf("%d", a[i]);
                    print(x);
                    break;
                }
            }
        }
        printf("\n");
    }
    return 0;
}
View Code
相關文章
相關標籤/搜索