2017 UESTC Training for Graph Theory

Posted 掉血菜鸡煮熟中

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017 UESTC Training for Graph Theory相关的知识,希望对你有一定的参考价值。

2017 UESTC Training for Graph Theory

A       思维

题意:给你一个有n个点和m条边的无向连通图,每条边都有一个权值w。我们定义,对于一条路径,它的Charm value为该路径上所有边的权值的最大值与最小值的差。询问从1到n的所有路径的Charm value的最小值。

tags:有点思维定式了。。一条路径里只要最大最小值,所以边可以重复走。这样我们只要把边从小到大枚举,把第 i 条边作为最小边,然后对于每个 i ,我们按顺序逐一加入比它大的边,直到点 1与点 n连通,更新ans即可。

技术分享
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 205, M = 1005;

int n, m, fa[N], ans=INF;
pair<int, pair<int ,int >  > e[M];
int Find(int x) { return fa[x]==x ? x : fa[x]=Find(fa[x]); }
void Unite(int u, int v)
{
    int fau=Find(u), fav=Find(v);
    if(fau!=fav) fa[fav]=fau;
}
void solve(int x)
{
    rep(i,1,n) fa[i]=i;
    rep(i,x,m)
    {
        Unite(e[i].se.fi, e[i].se.se);
        if(Find(1)==Find(n)) {
            ans=min(ans, e[i].fi-e[x].fi);
            return ;
        }
    }
}
int main()
{
    scanf("%d %d", &n, &m);
    int u, v, w;
    rep(i,1,m)
    {
        scanf("%d %d %d", &u, &v, &w);
        e[i]=MP(w, MP(u, v));
    }
    sort(e+1, e+1+m);
    rep(i,1,m)
    {
        solve(i);
    }
    printf("%d\n", ans);

    return 0;
}
View Code

B       建模转最短路

题意:给你一个大小为n的集合S,集合里有n个互不相同正整数。有q个询问,每次询问是否能选择S中的一些数字 ( 同一个数字可以选择多次,也可以任何数字都不选),使它们相加的和为m。

tags:好难想到取余数,转为最短路建模

选取基点a1,而dis[r]表示除a1余r的最小的数。这样我们可以把所有数都分为a1类,即除a1余 0~(a1-1)。然后初始是dis[0]=0加入队列,松驰操作是对每个ai,看能不能dis[(u+ai)%a1] > dis[u]+ai,即更新最短路。 最后询问的m,只要看dis[m%a1]是否 <=m即可。

技术分享
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define inf  1e18
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 500005;

int n, a[2005], q;
ll  m, dis[N];
bool inq[N];
void spfa(int st)
{
    mes(inq, 0);
    mes(dis, 63);  dis[0]=st;
    queue<int > Q;
    Q.push(st);  inq[st]=1;
    while(!Q.empty())
    {
        int  u=Q.front();  Q.pop();
        rep(i,2,n)
        {
            int to=(u+a[i])%a[1]; 
            if(dis[to] > dis[u]+a[i])
            {
                dis[to] = dis[u]+a[i];
                if(inq[to]==0) inq[to]=1, Q.push(to);
            }
        }
        inq[u]=0;
    }
}
int main()
{
    scanf("%d", &n);
    rep(i,1,n) scanf("%d", &a[i]);
    spfa(0);
    scanf("%d", &q);
    rep(i,1,q)
    {
        scanf("%d", &m);
        if(dis[m%a[1]] <= m) puts("YES");
        else puts("NO");
    }

    return 0;
}
View Code

C       dfs 01染色  或  欧拉路径

题意:二维平面上有n个点。你需要将每个点染成红色或蓝色。请设计一种染色方案能够使得每一行和每一列的红色的点的数量与蓝色的点的数量之差都不超过 1。

tags:很难想到思路,还是套路少。。

方法一: 如果某一行或某一列有k个点,那么可以对这k个点连k/2条边,使得每个点至多连了一条边。然后,对全图进行dfs染色,使得每条边两边的点的颜色不相同。

这个感觉比较难想,但好理解,也更好写。。

技术分享
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
#define PII  pair<int , int >
#define all(x)  x.begin(), x.end()
typedef long long ll;
const int N = 200005;

int n;
bool vis[N], ans[N];
vector<PII > X[N], Y[N];
struct Edge { int to, next; } e[N<<2];
int tot, head[N];
void Addedge(int u, int v) {
    e[tot]={v, head[u]}, head[u]=tot++;
    e[tot]={u, head[v]}, head[v]=tot++;
}
void dfs(int u, int flag)
{
    vis[u]=true, ans[u]=flag;
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        int to=e[i].to;
        if(vis[to]==false)
        {
            dfs(to, flag^1);
        }
    }
}
int main()
{
    mes(head, -1);
    scanf("%d", &n);
    int x, y;
    rep(i,1,n)
    {
        scanf("%d %d", &x, &y);
        X[x].PB(MP(y,i)), Y[y].PB(MP(x,i));
    }
    rep(i,1,N)
    {
        for(int j=1; j<X[i].size(); j+=2) {
            Addedge(X[i][j-1].se, X[i][j].se);
        }
        for(int j=1; j<Y[i].size(); j+=2) {
            Addedge(Y[i][j-1].se, Y[i][j].se);
        }

    }
    rep(i,1,n) if(vis[i]==false) dfs(i, 0);
    rep(i,1,n) putchar(ans[i] ? r : b);
    puts("");

    return 0;
}
/*
8
1 1
1 2
2 1
3 3
4 3
3 4
4 4
5 5
*/
View Code

方法二:   把每个点看成边,每个横纵坐标看成一个点,得到一个无向图。如果新图中每个点的度都是偶数,那么就是一个欧拉图,对该图跑一遍欧拉回路,对走过的边轮流染色,就可以保证每个点所连的边的红蓝颜色相等。如果存在度数为奇数的点,新建两个点a和b.把横坐标的度数为奇数的点和a连边,把纵坐标为奇数的点和b连边,这样最多只有a和b的度数为奇数,可以跑欧拉路径。

fleury(弗罗莱) 算法,有个好的地方,要及时把访问过的边从图中删去(是真的删去而不是打标记),否则重复访问会导致复杂度飙升。这样就只好用vector存图方便删除。

技术分享
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
#define PII  pair<int, int >
typedef long long ll;
const int N = 200005, M = N<<2;

int n, ans[M];
vector<PII > G[M];
bool vise[M], flag;
void Addedge(int u, int v, int x) {
    G[u].PB(MP(v, x));  G[v].PB(MP(u, x));
}
inline void dfs(int u)
{
    while(!G[u].empty())
    {
        PII e=G[u].back();
        G[u].pop_back();
        int to=e.fi, x=e.se;
        if(vise[x]==false)
        {
            vise[x]=true;
            dfs(to);
            if(0<x && x<=n) ans[x]=flag, flag^=1;
        }
    }
}
void fleury()
{
    int a=0, k=n;
    rep(i,1,N*2)
    {
        if(G[i].size()&1)
        {
            Addedge(a, i, ++k);
        }
    }
    rep(i,0,N*2) if(!G[i].empty())
        dfs(i);
}
int main()
{
    scanf("%d", &n);
    int x, y;
    rep(i,1,n)
    {
        scanf("%d %d", &x, &y);
        y += N;
        Addedge(x, y, i);
    }
    fleury();
    rep(i,1,n) putchar(ans[i] ? r : b);
    puts("");

    return 0;
}
View Code

I       欧拉回路,dfs

题意:构造一个01串,使其满足以下条件:环状(即首尾相连),每一位取值为0或1,长度是2n。对于每个(2n
个)位置,从其开始沿逆时针方向的连续的n位01串(包括自己)构成的数均不相同,即0到2n−1中的数各出现一次。
例如:n=3,样例的00010111,对于每个位置,沿逆时针方向连续长度为3的01串有:000,001,010,101,011,111,110,100,即为0−7的所有数字。

tags:按照规律连好边,dfs搜索即可。

技术分享
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 5000005;

int n, sta[N], top, s;
bool vis[N], flag;
vector<int > G[N];
int ans[N], k;
void dfs(int u)
{
    if(vis[u]==1) return ;
    if(top==s)
    {
        flag=1;   k=top;
        rep(i,0,top-1) ans[i]=sta[i];
        return ;
    }
    vis[u]=1;
    for(auto v : G[u]) if(vis[v]==0)
    {
        if(flag) return;
        sta[top++]=v;
        dfs(v);
        --top;
    }
    vis[u]=0;
}
int main()
{
    scanf("%d", &n);
    s=1<<n;
    rep(i,0,s-1)
    {
        int x=i>>1;
        if(x!=i) G[i].PB(x);
        x+= (1<<(n-1));
        if(x!=i) G[i].PB(x);
    }
    sta[top++]=0;
    dfs(0);
    printf("0");
    rep(i,1,k-1) printf("%d", ans[i]%2);

    return 0;
}
View Code

 

以上是关于2017 UESTC Training for Graph Theory的主要内容,如果未能解决你的问题,请参考以下文章

2017 UESTC Training for Dynamic Programming

2017 UESTC Training for Graph Theory

2017 UESTC Training for Search Algorithm & String

2017 UESTC Training for Dynamic Programming

2017 UESTC Training for Graph Theory

2016 UESTC Training for Dynamic Programming