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. MKMapView的使用

    #import "ViewController.h" #import "BVAnnotation.h" @interface ViewController () ...

  2. day5----模块

    1.定义     模块:用来从逻辑上组织python代码(变量,函数,类,运行逻辑:实现一个功能),本质就是.py结尾的python文件(文件名:test.py,对应的模块名:test)     包: ...

  3. 【整理】--VC 编译整理

    .h用于编译阶段的审核,如在math.h中有函数声明:int abs(int);但是在使用中写为#include <math.h>...abs(3,5);编译器阶段就会检测出错误. .dl ...

  4. SQLSERVER2014 2014年4月1日发布

    SQLSERVER2014 2014年4月1日发布 原文地址: http://blogs.technet.com/b/microsoft_blog/archive/2014/03/18/sql-ser ...

  5. 更改vsts源代码绑定服务器地址方法

    第一步找到更改源代码管理 第二步首先选中第一个,滚动条拉至最后一项,点击最后一项(全选). 第三步点击绑定,vsts会自动签出所有项目,而后签入就可以更改成功了.

  6. ASP.NET 连接MySQL数据库 详细步骤

    ASP.NET默认的数据库是MS SQL Server,微软的数据库产品.事实上,如果不计成本因素的话,Windows Server + IIS + MS SQL Server + ASP.NET是网 ...

  7. 用c#开发微信 (10) JS-SDK 基本用法- 分享接口“发送到朋友”

    微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包.通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统的能力,同时可以直接使用微信分享. ...

  8. starUML使用教程

    starUML使用教程 下载与安装 先从官网下载软件,这个版本是收费的,但可以先进行试用.也可以使用免费版,基本UML依然齐全,但用起来感觉没有收费版好用. 下载之后按照安装说明,一步步安装就可以了. ...

  9. solr与.net系列课程(一)solr的安装与配置

    不久之前开发了一个项目,需要用到solr,因为所以在开始再网上查找资料,但是发现大部分的资料都是很片面的,要么就是只讲解solr如何安装的,要么就是只讲解solr的某一个部分的,而且很多都是资料都是一 ...

  10. Vertica笔记

    1.Table不做存储,本质是一个视图,真正存储在 Projection 里.可以针对一个Table建多个Projection . 查看表和 Projection 的个数: select anchor ...