A:梦后楼台高锁,酒醒帘幕低垂
题目链接:http://acm.uestc.edu.cn/#/problem/show/1636
解法:首先,考虑到,我们需要找到一条路径,使它的最小边尽量大,最大边尽量小。然后,考虑到m比较小,我们可以去寻找一个m^2或者m^2logm的算法。考虑枚举最小边,那么我们就需要在m或者mlogm的时间内找到尽量小的最大边.回忆最小生成树的kruskal算法,并查集+贪心加边.应用到此题,从枚举的最小边贪心加边,当1和n属于同一个集合时停止,得出的一定是当前最小边情况下的最优解
#include <bits/stdc++.h>
using namespace std;
struct edge{
int u,v,w;
}E[1010];
int n,m;
int fa[210],edgecnt;
bool cmp(edge a, edge b){
return a.w<b.w;
}
int find_set(int x){
if(x==fa[x]) return x;
else return fa[x]=find_set(fa[x]);
}
void union_set(int x, int y){
int fx=find_set(x),fy=find_set(y);
if(fx!=fy){
fa[fx]=fy;
}
}
int main()
{
while(~scanf("%d %d",&n,&m)){
for(int i=1; i<=n; i++) fa[i]=i;
for(int i=1; i<=m; i++){
scanf("%d %d %d", &E[i].u,&E[i].v,&E[i].w);
}
sort(E+1,E+m+1,cmp);
int ans=1e9;
for(int i=1; i<=m; i++){
for(int i=1; i<=n; i++) fa[i]=i;
union_set(E[i].u,E[i].v);
if(find_set(1)==find_set(n)){
ans=0;
break;
}
for(int j=i+1; j<=m; j++){
union_set(E[j].u,E[j].v);
if(find_set(1)==find_set(n)){
ans=min(ans,E[j].w-E[i].w);
break;
}
}
}
printf("%d\n", ans);
}
return 0;
}
B:去年春恨却来时,落花人独立,微雨燕双飞
题目链接:http://acm.uestc.edu.cn/#/problem/show/1633
解法:
对于S集合中的数,例如a1,考虑到如果x能够被表示出来,那么x+a1也一定能被表示出来。
故设d[r]为所有模a1余r的数中,能被表示出来的最小的数。
故可以表示出一个a1个节点a1*n条边的有向图。
d[0] = 0,丢入priority_queue<ii, vector<ii>, greater<ii>>,然后对于pq.top()的点连出的n条边进行松弛操作。
如果 d[u] + a[i] < d[(d[u] + a[i])] 则刷新并丢入队列。
跑出来的d[i],如果d[i] != INF则都是可以表示出来的。
对于每个询问判断 q >= d[q % a[1]]即可。
时间复杂度 O(mlogn)
空间复杂度 O(n), 不用存边。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2010;
const int maxm = 50010;
const int inf = 1e9+8;
int a[maxn], dis[maxm];
bool vis[maxm];
int n,m,x;
priority_queue<pair<int,int>,vector<pair<int,int> >, greater<pair<int,int> > >q;
void Dij()
{
while(!q.empty()) q.pop();
for(int i=0; i<maxm; i++) dis[i]=inf,vis[i]=0;
memset(vis,false,sizeof(vis));
dis[0]=0;
q.push(make_pair(0,0));//first->num,second->num%a[1]
while(!q.empty()){
int u = q.top().second;
int d = q.top().first;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=1; i<=n; i++){
if(d+a[i]<dis[(d+a[i])%a[1]]){
dis[(d+a[i])%a[1]] = d+a[i];
q.push(make_pair(d+a[i],(d+a[i])%a[1]));
}
}
}
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
Dij();
scanf("%d", &m);
while(m--){
scanf("%d", &x);
if(x>=dis[(x%a[1])]){
puts("YES");
}
else{
puts("NO");
}
}
}
return 0;
}
C:记得小苹初见,两重心字罗衣
题目链接:http://acm.uestc.edu.cn/#/problem/show/1634
解法:把每个点看成边,每个横纵坐标看成一个点,得到一个无向图.如果新图中每个点的度都是偶数,那么就是一个欧拉图,对该图跑一遍欧拉回路,对走过的边轮流染色,就可以保证每个点所连的边的红蓝颜色相等.如果存在度数为奇数的点,新建两个点a和b.把横坐标的度数为奇数的点和a连边,把纵坐标为奇数的点和b连边,这样最多只有a和b的度数为奇数,可以跑欧拉路径.
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int maxn = 2e5+6;
const int maxm = 3*2e5+6;
vector <pii> G[maxn*3];
vector <int> ans;
bool vis[maxm];
int anss[maxm];
int n;
inline void add(int x, int y, int z){
G[x].push_back(pii(z,y));
G[y].push_back(pii(z,x));
}
inline void dfs(int u){
int v, e;
while(!G[u].empty()){
v = G[u].back().second, e = G[u].back().first;
G[u].pop_back();
if(!vis[e]){
vis[e] = 1;
dfs(v);
ans.push_back(e);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++){
int x, y;
scanf("%d %d", &x, &y);
add(x, y+maxn, i);
}
int st = -1, a = 2*maxn+1, b = a+1, cnt = n;
for(int i=1; i <= 2*maxn; i++){
int sz = G[i].size();
if(sz){
if(sz&1){
if(i<=maxn){
add(a, i, ++cnt);
if(st != a) st = a;
}
else{
add(b, i, ++cnt);
if(st != b) st = b;
}
}
if(st == -1) st = i;
}
}
dfs(st);
for(int i = 1; i <= 2*maxn; i++){
if(G[i].size()){
dfs(i);
}
}
int sz = ans.size();
for(int i = 0; i < sz; i++){
if(ans[i] <= n){
anss[ans[i]] = i&1;
}
}
for(int i=1; i <=n ; i++){
if(anss[i]) putchar('r');
else putchar('b');
}
return 0;
}
D:琵琶弦上说相思,当时明月在,曾照彩云归
题目链接:http://acm.uestc.edu.cn/#/problem/show/1635
解法:
拓扑排序、bfs
对于相邻的2个字符串当第一次遇到不相等的字符是前一个u必定比比后一个v小,
所以可以u向v连一条有向边。
然后对于建出的这个图,跑一次拓扑排序即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int maxn = 1010;
char s[maxn][300];
vector <int> G[30];
queue <int> q1;
priority_queue <pii, vector<pii>, greater<pii> > q2;
int du[30];
bool vis[maxn];
void topsort()
{
while(!q2.empty()){
int u = q2.top().first;
int fa = q2.top().second;
q2.pop();
q1.push(u);
vis[u] = 1;
for(int i = 0; i < G[u].size(); i++){
int v = G[u][i];
if(v == fa) continue;
du[v]--;
if(du[v] == 0){
q2.push(pii(v, u));
}
}
}
}
int main()
{
int n, len1, len2;
scanf("%d", &n);
bool ans = 1;
for(int i = 0; i < n; i++) scanf("%s", s[i]);
for(int i = 0; i < n- 1; i++){
len1 = strlen(s[i]);
len2 = strlen(s[i+1]);
bool flag = 0;
for(int j = 0; j < min(len1, len2); j++){
if(s[i][j] !=s[i+1][j]){
flag = 1;
G[s[i][j]-'a'].push_back(s[i+1][j]-'a');
break;
}
}
if(flag == 0){
if(len1 > len2){
ans = 0;
break;
}
}
}
if(ans == 0){
puts("-1\n");
}
else{
for(int i = 0; i < 26; i++){
for(int j = 0; j < G[i].size(); j++){
du[G[i][j]]++;
}
}
for(int i = 0; i < 26; i++){
if(G[i].size() && du[i] == 0){
q2.push(pii(i, -1));
}
}
topsort();
for(int i = 0; i < 26; i++){
if(du[i] != 0){
ans = 0;
break;
}
}
if(ans == 0){
puts("-1");
}
else{
int i = 0;
while(!q1.empty()){
while(vis[i]) i++;
while(i < q1.front()){
putchar(char('a'+i));
i++;
while(vis[i]) i++;
}
putchar(char('a'+q1.front())); q1.pop();
}
while(vis[i]) i++;
while(i < 26){
putchar(char('a' + i));
i++;
}
printf("\n");
}
}
return 0;
}