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. 自然数的K次幂的数列求和

        ------------------------------------------------------------------------------- 转载请注明出处 博客园 刺猬的温 ...

  2. UITableView表格视图、UITableView代理方法及应用

    一.基本知识点 UITableView表格视图,是一个可以滚动的界面(理解为垂直滚动的UIScrollView),可展示多行数据,没有行数的限制,只能有一列. 使用UITableView: 1.展示信 ...

  3. codeforces 424D

    题意:给定n,m<=300的矩阵,然后求一个长宽大于2的矩形,使得是从左上角位置开始逆时针绕边框一圈的时间最少.时间的计算是:给定3个数tu,tp, td,路径上数字增加为tu,相等为tp否则为 ...

  4. codeforces 425C

    题意:给定长度为n,m<=100000的范围在100000以内的数组a,b. 现在给定两种操作: 第一种是ai,bj相等,ai,bj之前的数全删掉,费用为e,收益为1 第二种是把剩下的全部删掉, ...

  5. Q114第一颗二叉查找树(链式)

    输入n,然后n个树,建立二叉查找树.从小到大输出每个节点的左右子树,空输出# #include<cstdio> #include<iostream> using namespa ...

  6. 解决linux下oracle-11g打不开dbca问题

    linux下oracle安装完毕后,出现建立数据库命令dbca无法使用问题,如图: 解决办法: 在32位的linux环境下,安装32位oracle11g会有这个bug,主要装个补丁(patch nam ...

  7. django rest_framework

    环境 django 1.6,rest_framework 3.3 ubuntu采用pip安装的rest_framework 按照例子一步步做下来 运行 提示filters.py第119行有错误form ...

  8. 微软BI 之SSIS 系列 - 再谈Lookup 缓存

    开篇介绍 关于 Lookup 的缓存其实在之前的一篇文章中已经提到了 微软BI 之SSIS 系列 - Lookup 组件的使用与它的几种缓存模式 - Full Cache, Partial Cache ...

  9. 3.SRS文档

    1.功能需求 本程序的使用者为局域网用户.程序实现的主要功能是局域网的常见格式的文件的传 输.其用例图如图1.本程序可通过可视化操作界面实现一对多的文件传输. 1.1模块分析 为实现局域网文件传输, ...

  10. 团队项目--站立会议 DAY4

    小组名称:D&M 参会人员:张靖颜,钟灵毓秀,何玥,赵莹,王梓萱 项目进展: 1.张靖颜:筛选完元素后,把昨天优化完的再进行测试,进行进一步的检验.对于钟灵毓秀和赵莹同学编写的代码进行进一步审 ...