10457 - Magic Car

题意一开始看起来有点费解,其实最后就是要起点到终点的路径上最大边与最小边之差越小越好。这样我们可以先将边排个序,然后枚举路径上的最小边,之后依次将比它大的边按升序的顺序添加,直到起点和重点连通为止,这样就得到了一个可行解并尝试去更新记录的最优解。等所有可能的最小边都枚举完之后就可以得到最终的最优解了。

#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 = ; 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 = ; i < M; i ++)
{
int j;
for(int j = ; 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) == )
{
input();
process();
}
return ;
}

10607 - Siege

又是一个神一样的题意,我再次没读懂,不过根据种种迹象来揣测的话,应该是可以先占领边疆城市,然后就可以占领和已占领的城市相邻的城市了,直到能够围住首都。这个题目里面有个比较特殊的条件,就是“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的论坛上找到了一种情况:

5 5

CCCCC

CAAAC

CABAC

CAAAC

CCCCC

这种情况就应当输出-1了,因为根据题意B应该是和A相邻的,但是我们没办法通过给定的规则占领B,所以输出-1。

其次,题目的数据里面是有空格的,还有一些>127的ASC码,这些比较特殊的字符也全当正常的城市去处理就好了。

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

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][], R;
const int dx[] = {-, , , }, dy[] = {, , -, }, fac[] = {, , , };
int getMax(int a[])
{
int ans = a[];
for(int i = ; i < ; i ++) ans = std::max(ans, a[i]);
return ans;
}
struct St
{
int x, y, a[], max, code;
St(){}
St(int _x, int _y) : x(_x), y(_y)
{
code = max = ;
memset(a, , sizeof(a));
}
}q[];
void input()
{
char s[MAXN];
for(int i = ; i < N; i ++)
{
scanf("%s", s);
for(int j = ; j < N; j ++)
a[i][j] = s[j] == 'R';
}
}
bool inside(int x, int y)
{
return x >= && x < N && y >= && y < N;
}
void process()
{
const int ox = N >> , oy = N >> , most = N >> ;
int front = , rear = ;
vis[ox][oy][] = R, q[rear ++] = St(ox, oy);
int ans = INF;
while(front < rear)
{
St st = q[front ++];
if(st.max >= ans) continue; for(int i = ; i < ; 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[], nst.code += fac[];
if(a[-bx + ox][-by + oy]) ++ nst.a[], nst.code += fac[];
if(a[-by + ox][bx + oy]) ++ nst.a[], nst.code += fac[];
if(a[by + ox][-bx + oy]) ++ nst.a[], nst.code += fac[];
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 = ;
while(scanf("%d", &N), N)
{
++ R;
input();
process();
}
return ;
}

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[];
const int Q = ;
int N;
std::string x, y, s[MAXN];
void input()
{
std::cin >> x >> y;
std::cin >> N;
for(int i = ; i < N; i ++)
std::cin >> s[i];
}
void process()
{
int front, rear;
front = rear = ;
if(x.size() < y.size()) std::swap(x, y);
if(x == y)
{
std::cout << "0\n";
return ;
}
int n = y.size();
if(x.substr(, n) != y)
{
std::cout << "-1\n";
return ;
}
std::set<std::string> set;
std::string a = x.substr(n, x.size() - n);
q[rear ++] = State(, a), set.insert(a); while(front != rear)
{
State st = q[front ++];
if(front >= Q) front = ;
n = st.s.size();
for(int i = ; 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(, n) != st.s) over = true;
else a = s[i].substr(n, nn - n);
}
else
{
if(st.s.substr(, 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 + , a);
if(rear >= Q) rear = ;
}
}
}
std::cout << "-1\n";
}
int main()
{
int t;
std::cin >> t;
while(t --)
{
input();
process();
}
return ;
}

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 = -, t = -;
memset(del, , sizeof(del[]) * (N + ));
for(int i = ; i <= N; i ++)
{
if(dgr[i] <= )
{
printf("Poor Koorosh\n");
return ;
}
if(dgr[i] == ) del[i] = true;
else if(dgr[i] & )
{
if(s == -) s = i;
else t = i;
}
}
if(s == -)
{
printf("0\n");
return ;
}
memset(dis, 0x3f, sizeof(dis[]) * (N + ));
int rear = ;
dis[s] = , q[rear ++] = s;
for(int i = ; i < rear; i ++)
for(int j = first[q[i]]; j != -; j = next[j])
if(!del[v[j]] && dis[q[i]] + < dis[v[j]])
dis[v[j]] = dis[q[i]] + , 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, , sizeof(dgr[]) * (N + ));
memset(first, -, sizeof(first[]) * (N + )), e = ;
for(int i = ; i < M; i ++)
{
int x, y;
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
++ dgr[x], ++ dgr[y];
}
process();
}
return ;
}

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[];
bool operator < (const Seg &t) const
{
if(u != t.u) return u < t.u;
if(v[] != t.v[]) return v[] < t.v[];
return v[] < t.v[];
}
}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 = ;
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 = ; i < K; i ++)
{
scanf("%d%d-%d", &fb[i].u, &fb[i].v[], &fb[i].v[]);
if(fb[i].v[] > fb[i].v[]) std::swap(fb[i].v[], fb[i].v[]);
}
scanf("%d%d", &S, &T);
}
void initSeg()
{
std::sort(fb, fb + K);
memset(first, -, sizeof(first[]) * N), e = ;
memset(cal, , sizeof(cal[]) * N);
int last = -, u;
Seg s;
for(int i = ; i < K; i ++)
{
u = fb[i].u, cal[u] = true;
if(fb[i].v[] > last + )
{
s.v[] = last + , s.v[] = fb[i].v[] - ;
add(u, s);
}
last = std::max(last, fb[i].v[]);
if(i == K - || fb[i + ].u != fb[i].u)
{
if(last + <= N - )
{
s.v[] = last + , s.v[] = N - ;
add(u, s);
}
last = -;
}
}
for(int i = ; i < N; i ++)
if(!cal[i])
{
s.v[] = , s.v[] = N - ;
add(i, s);
}
}
void process()
{
initSeg();
dis[T] = -;
for(int i = ; i <= N; i ++) p[i] = i;
int rear = ;
dis[S] = , q[rear ++] = S;
merge(S, S + );
for(int i = ; i < rear; i ++)
for(int j = first[q[i]]; j != -; j = next[j])
{
int x = seg[j].v[], y = seg[j].v[];
for(int u = find(x); u <= y; u = find(u))
{
dis[u] = dis[q[i]] + , q[rear ++] = u;
merge(u, u + );
}
} if(dis[T] == -) printf("Impossible\n");
else printf("%d\n", dis[T]);
}
int main()
{
int t;
scanf("%d", &t);
for(int tt = ; tt <= t; tt ++)
{
input();
printf("Case #%d: ", tt);
process();
}
return ;
}

11853 - Paintball

首先考虑无解的情况,那么必然是从上到下若干个圆连在了一起把路截断了。那么我们就可以将圆转化成点,并虚拟一个起点和终点,凡是和上边界相交的圆都和起点相连,和下边界相交的圆都和终点相连,并且相交的圆之间也连起来,这样如果从起点出发可以到达终点,那么途径的圆就会截断我们的通路。

解决完无解的情况后,我们可以用类似的思想求解从哪里可以进去以及从哪里可以出去。

不妨先考虑从哪里可以进去,但是这个不大好考虑,于是转而去考虑哪里不可以进去。对于任意一个和左边界相交的圆来讲,如果从这个圆出发,按之前建好的图走能走到上边界的话,那么这个圆的弦,以及弦上面的部分都是不可以进去的。同理,如果从这个圆出发,能走到下边界的话,那么这个圆的弦以及弦下面的部分都是不可以进去的。最后如果从这个圆出发既不能走到上边界,也不能走到下边界,那么就只有这个圆的弦这部分是不可以进去的。这样我就能够处理出来若干个不能进去的位置的区间,根据这些区间就不难找到可能的最下面的可以进去的位置了。

考虑从哪里可以出去是类似的,就不再赘述了。

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

 

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[], d[], q[][], qn[], qm[];
bool vis[];
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 + , k;
for(int i = ; i < m; i ++)
{
int t;
if(qn[i] == ) t = sv - q[i][] - q[i][] + * qm[i];
else t = sv - q[i][qn[i] - ] - q[i][qn[i] - ];
if(t < min) min = t, k = i;
} int max1 = -, max2 = -, k1, k2;
for(int i = ; i < m; i ++)
if(q[i][qn[i] - ] > max1)
max1 = q[i][qn[i] - ], k1 = i;
for(int i = ; i < m; i ++)
if(i != k1 && q[i][qn[i] - ] > max2)
max2 = q[i][qn[i] - ], k2 = i; if(sv - max1 - max2 < min)
-- qn[k1], -- qn[k2], sv -= max1 + max2, sn -= ;
else
{
if(qn[k] == ) q[k][] = q[k][] = qm[k], sv = min;
else qn[k] -= , sv = min, sn -= ;
}
}
int main()
{
int n;
while(scanf("%d", &n) == )
{
memset(d, , sizeof(d));
for(int i = ; i <= ; i ++) p[i] = i;
while(n --)
{
int x, y;
scanf("%d%d", &x, &y);
merge(x, y);
++ d[x], ++ d[y];
} memset(vis, , sizeof(vis));
int m = , sn = , sv = ;
for(int i = ; i <= ; i ++)
if(d[i] > && !vis[find(i)])
{
vis[find(i)] = true;
qn[m] = , qm[m] = i;
for(int j = i; j <= ; j ++)
if(find(i) == find(j))
if(d[j] & ) q[m][qn[m] ++] = j, sv += j;
sn += qn[m];
std::sort(q[m], q[m] + qn[m]);
++ m;
} if(m == )
{
if(sn >= ) sv -= q[][sn - ] + q[][sn - ], sn -= ;
printf("%d\n", sv);
printf("%d\n", sn / );
for(int i = ; i < sn; i += )
printf("%d %d\n", q[][i], q[][i + ]);
continue;
} for(int i = ; i < m; i ++)
if(qn[i] == )
q[i][] = q[i][] = qm[i], qn[i] = , sn += , sv += qm[i] * ;
makeChoice(m, sv, sn);
printf("%d\n%d\n", sv, sn / ); int a[], an = , o1, o2;
for(o1 = ; o1 < m && (qn[o1] & ) == ; o1 ++);
for(o2 = o1 + ; o2 < m && (qn[o2] & ) == ; o2 ++);
if(o1 < m) a[an ++] = o1;
for(int i = ; i < m; i ++)
if((qn[i] & ) == ) a[an ++] = i;
if(o2 < m) a[an ++] = o2;
for(int i = ; i < m - ; i ++)
{
int x = a[i], y = a[i + ];
printf("%d %d\n", q[x][-- qn[x]], q[y][-- qn[y]]);
} an = ;
for(int i = ; i < m; i ++)
for(int j = ; j < qn[i]; j ++)
a[an ++] = q[i][j];
for(int i = ; i < an; i += )
printf("%d %d\n", a[i], a[i + ]);
}
return ;
}

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[], d[], r[], q[];
bool can[];
void print(int x)
{
if(x == ) return ;
for(int i = ; i < M; i ++)
{
int s = (x * + a[i]) % N;
if(can[s] && d[s] == d[x] + )
{
printf("%d", a[i]);
print(s);
break;
}
}
}
int main()
{
while(scanf("%d%d", &N, &M) == )
{
for(int i = ; i < M; i ++) scanf("%d", &a[i]);
if(N == )
{
printf("0\n");
continue;
}
std::sort(a, a + M);
memset(d, 0x3f, sizeof(d[]) * N);
std::vector<int> v[];
int rear = ;
for(int i = ; i < M; i ++)
{
if(a[i] == ) continue;
int x = a[i] % N;
if( < d[x]) d[x] = , q[rear ++] = x;
}
for(int i = ; i < rear; i ++)
{
int x = q[i];
for(int j = ; j < M; j ++)
{
int s = (x * + a[j]) % N;
if(d[x] + < d[s]) d[s] = d[x] + , q[rear ++] = s, v[s].push_back(x);
else if(d[x] + == d[s]) v[s].push_back(x); }
}
if(d[] == INF) printf("");
else
{
memset(can, , sizeof(can[]) * N);
rear = ;
can[] = true, q[rear ++] = ;
for(int i = ; i < rear; i ++)
{
int x = q[i];
for(int j = ; j < v[x].size(); j ++)
{
int y = v[x][j];
if(!can[y]) can[y] = true, q[rear ++] = y;
}
}
for(int i = ; i < M; i ++)
{
if(a[i] == ) continue;
int x = a[i] % N;
if(can[x])
{
printf("%d", a[i]);
print(x);
break;
}
}
}
printf("\n");
}
return ;
}

Chapter 5. Graph Theory:: Fundamentals:: Intermediate的更多相关文章

  1. Introduction to graph theory 图论/脑网络基础

    Source: Connected Brain Figure above: Bullmore E, Sporns O. Complex brain networks: graph theoretica ...

  2. The Beginning of the Graph Theory

    The Beginning of the Graph Theory 是的,这不是一道题.最近数论刷的实在是太多了,我要开始我的图论与树的假期生活了. 祝愿我吧??!ShuraK...... poj18 ...

  3. Codeforces 1109D Sasha and Interesting Fact from Graph Theory (看题解) 组合数学

    Sasha and Interesting Fact from Graph Theory n 个 点形成 m 个有标号森林的方案数为 F(n, m) = m * n ^ {n - 1 - m} 然后就 ...

  4. CF1109D Sasha and Interesting Fact from Graph Theory

    CF1109D Sasha and Interesting Fact from Graph Theory 这个 \(D\) 题比赛切掉的人基本上是 \(C\) 题的 \(5,6\) 倍...果然数学计 ...

  5. HDU6029 Graph Theory 2017-05-07 19:04 40人阅读 评论(0) 收藏

    Graph Theory                                                                 Time Limit: 2000/1000 M ...

  6. Codeforces 1109D. Sasha and Interesting Fact from Graph Theory

    Codeforces 1109D. Sasha and Interesting Fact from Graph Theory 解题思路: 这题我根本不会做,是周指导带飞我. 首先对于当前已经有 \(m ...

  7. 2018 Multi-University Training Contest 4 Problem L. Graph Theory Homework 【YY】

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6343 Problem L. Graph Theory Homework Time Limit: 2000 ...

  8. Graph Theory

    Description Little Q loves playing with different kinds of graphs very much. One day he thought abou ...

  9. 2017中国大学生程序设计竞赛 - 女生专场(Graph Theory)

    Graph Theory Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)To ...

随机推荐

  1. C/C++中各种类型int、long、double、char表示范围(最大最小值)(转)

    #include<iostream> #include<string> #include <limits> using namespace std; int mai ...

  2. iOS ViewController生命周期

    ViewController是view的controller,viewController的职责主要包括管理内部各个view的加载显示与卸载,同时负责与其他ViewController的通信和协调. ...

  3. LDO-BD00C0AWFP

    BD00C0AWxx  1.该产品是ROHM公司的一款高输入电压可达26.5V,输出电压可达15V,输出电流1A的LDO.具有较低的ESR Capacitor. 2.输入电压变化4-26.5V,输出3 ...

  4. JAVA的String的传值和传地址问题

    关于Java的String类型,可能你会碰到这种情况,将String类型的变量传到一个函数,在这个函数中修改变量的值,但是,实参的值并没有发生改变. Java中String的传值/传地址问题: 例子引 ...

  5. libqrencode 3.4.3 发布,二维码的C解析库

    libqrencode 3.4.3 的命令行增加了 --rle 参数,修复了开发库和命令行工具的一些小 bug. libqrencode (QRencode) 是一个用C语言编写的用来解析二维条形码( ...

  6. PHP微信墙制作,开源

    PHP微信墙制作 微信墙 PHP 注意:由于微信官网不定时会更新,其中模拟登陆以及爬取数据的方式可能会失效,最近这12个月里,就有两次更新导致此功能需要重写. 服务端源码->github地址传送 ...

  7. C#设计模式(16)——迭代器模式(Iterator Pattern)

    一.引言 在上篇博文中分享了我对命令模式的理解,命令模式主要是把行为进行抽象成命令,使得请求者的行为和接受者的行为形成低耦合.在一章中,将介绍一下迭代器模式.下面废话不多说了,直接进入本博文的主题. ...

  8. node-webkit教程(10)Platform Service之File dialogs

    node-webkit教程(10)Platform Service之File dialogs 文/玄魂 目录 node-webkit教程(10)Platform Service之File dialog ...

  9. [MSSQL2008]Spatial Data in SQL Server 2008 - 根据经纬度计算两点间距离

    DECLARE @BJ GEOGRAPHY DECLARE @XT GEOGRAPHY /*     GET Latitude/Longitude FROM here:http://www.trave ...

  10. [WinAPI] API 10 [创建、打开、读写文件,获取文件大小]

    在Windows系统中,创建和打开文件都是使用API函数CreateFile,CreateFile通过指定不同的参数来表示是新建一个文件,打开已经存在的文件,还是重新建立文件等.读写文件最为直接的方式 ...