新生专题三(图论)

知识点:最短路问题, 最小生成树, 拓扑排序, 并查集等.

题目链接: 点击打开链接

一.Good Luck in CET-4 Everybody!(HDU1847)

这道题我一看是博弈论,一脸懵逼,说好的图论,,,,,,我是找规律过的反正.
百度下来叫巴什博弈, 就是三的倍数先手稳输.
#include <cstdio>

int main()
{
    int n;
    while(scanf("%d", &n) != EOF)
    {
        if(n % 3){
            printf("Kiki\n");
        }
        else{
            printf("Cici\n");
        }
    }

    return 0;
}

二.Highways(POJ1751)

最小生成树.Kruskal算法.(百度了一下,Prim也有人写,  但是可能超时)

我是套的牛客网讲课学长给的板子.

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
//MAXN是最大点数,MAXM是最大边数
const int MAXN=4000;//一开始小了RE了,一怒之下开这么大
const int MAXM=500000;
int F[MAXN];//并查集
struct Edge
{
    int u,v,w;
} edge[MAXM];///存路
int tol;//边的总数
void addedge(int u,int v,int w)
{
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol++].w=w;
}
bool cmp(Edge a,Edge b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(F[x]==-1)return x;
    else return F[x]=find(F[x]);//路径压缩
}

///用并查集来维护已经连起来的关系。
int Kruskal(int n)
{
    memset(F,-1,sizeof(F));
    sort(edge,edge+tol,cmp);///对路排序
    int cnt=0;///cnt是计数已经选了的路
    int ans=0;
    for(int i=0; i<tol; i++)
    {
        int u=edge[i].u;
        int v=edge[i].v;
        int w=edge[i].w;
        int t1=find(u);
        int t2=find(v);
        if(t1!=t2)
        {  //最大的一条边
            ans=max(ans,w);
            F[t1]=t2;
            cnt++;
        }
        if(cnt==n-1)break;
    }
    if(cnt<n-1)return -1;
    else return ans;
}
int main()
{
    int n;
    int T;
    scanf("%d", &T);

    while(T--)
    {
        scanf("%d",&n);
        memset(edge,0,sizeof(edge));
        tol=0;
        for (int i=0; i<n; i++)
        {
            int u = i, v, w;
            for(int j = 0; j < n; j++)
            {
                v = j;
                scanf("%d", &w);
                if(w){///输入跳过0
                    addedge(u,v,w);
                }
            }
        }

        int ans=Kruskal(n);
        printf("%d\n",ans);

    }

    return 0;
}

三.Connect the Cities(HDU3371)


我Kruskal 和 Prim 都写了, 都超时了.百度了一个980ms的代码, 数据至于卡这么死吗.

#include <cstdio>
#include <cstring>
using namespace std;
#define MAXN 505
#define INF 10000000
bool vis[MAXN];
int lowc[MAXN];
int n, m, k;
int maps[MAXN][MAXN];

int prim()
{
    int ans = 0;
    memset(vis, false, sizeof vis);
    vis[1] = true;

    for(int i = 1; i <= n; i++)
        lowc[i] = maps[1][i];

    for(int i = 2; i <= n; i++){
        int minc = INF;
        int p = -1;
        for(int j = 1; j <= n; j++)
            if(!vis[j] && minc>lowc[j])
        {
            minc = lowc[j];
            p = j;
        }

        if(minc >= INF)
            return -1;

        ans += minc;

        vis[p] = true;

        for(int j = 1; j <= n; j++){
            if(!vis[j] && lowc[j] > maps[p][j])
                lowc[j] = maps[p][j];
        }
    }

    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);

    while(T--)
    {
        scanf("%d%d%d", &n, &m, &k);
        memset(maps, 0x3f, sizeof maps);

        for(int i = 1; i <= m; i++){
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            if(maps[u][v] > w)
            maps[u][v] = maps[v][u] = w;
        }

        for(int i = 1; i <= k; i++){
            int t, a[MAXN];
            scanf("%d", &t);
            for(int j = 1; j <= t; j++)
                scanf("%d", &a[j]);
            for(int j = 1; j <= t; j++)
                for(int l = 1; l <= t; l++)
                    maps[a[j]][a[l]] = 0;
        }

        printf("%d\n", prim());
    }

    return 0;
}

/**
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=505;
const int Max=0x3f3f3f3f;
int map[maxn][maxn],low[maxn],visit[maxn];
int n,m,k;
void prim()
{
    int i,j,pos,min,mst=0;
    memset(visit,0,sizeof(visit));
    pos=1;
    visit[1]=1;
    for(i=1;i<=n;i++)
        low[i]=map[pos][i];
    for(i=1;i<n;i++)
    {
        min=Max;
        for(j=1;j<=n;j++)//更新min的值
        {
            if(!visit[j] && min>low[j])
            {
                min=low[j];
                pos=j;
            }
        }
        mst+=min;
        if(mst>=Max) break;//说明这个图不连通
        visit[pos]=j;
        for(j=1;j<=n;j++)
        {
            if(!visit[j] && low[j]>map[pos][j])//更新low数组
                low[j]=map[pos][j];
        }
    }
    if(mst>=Max)
        printf("-1\n");
    else
        printf("%d\n",mst);
}
int main()
{
    int t,i,j,x;
    int p,q,c;
    int a[101];
    scanf("%d",&t);
    while(t--)
    {
        memset(map,Max,sizeof(map));
        scanf("%d%d%d",&n,&m,&k);
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&p,&q,&c);
            if(map[p][q]>c)//这里也要注意一下,题目没说有重边,但是数据里面有,没加这个就wa
            map[p][q]=map[q][p]=c;
        }
        while(k--)//处理已连接的边,把权值赋0;
        {
            scanf("%d",&x);
            for(i=1;i<=x;i++)
                scanf("%d",&a[i]);
            for(i=1;i<=x;i++)
                for(j=1;j<=x;j++)
                    map[a[i]][a[j]]=0;
        }
       prim();
    }
    return 0;
}
*/


/**
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 505
#define MAXM 25005
int father[MAXN];
int rak[MAXN];
struct Edge{
  int u, v, w;
}edge[MAXM];
int tol;
int cnt;

void addedge(int u, int v, int w)
{
    edge[tol].u = u;
    edge[tol].v = v;
    edge[tol++].w = w;
}

bool cmp(Edge a, Edge b)
{
    return a.w < b.w;
}

void init(int n)
{
    for(int i = 0; i <= n; i++){
        father[i] = i;
        rak[i] = 0;
    }
}

int findfa(int x)
{
    if(father[x] = x) return x;
    else return father[x] = findfa(father[x]);
}

void unite(int x, int y)
{
    x = findfa(x);
    y = findfa(y);

    if(x != y)
        cnt++;

    if(rak[x] < rak[y])
        father[x] = y;
    else{
        father[y] = x;
        if(rak[x] == rak[y])
            rak[x]++;
    }
}

int Kruskal(int n)
{
    sort(edge, edge + tol, cmp);
    int ans = 0;

    for(int i = 0; i < tol; i++){
        int rot1 = findfa(edge[i].u);
        int rot2 = findfa(edge[i].v);
        if(rot1 != rot2){
            ans += edge[i].w;
            unite(rot1, rot2);
        }
        if(cnt == n - 1)
            break;
    }
    if(cnt < n - 1) return -1;
    else return ans;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        tol = 0;

        for(int i = 0; i < m; i++){
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            addedge(u, v, w);
        }

        init(n);
        cnt = 0;
        for(int i = 0; i < k; i++){
            int t, l;
            scanf("%d%d", &t, &l);

            for(int j = 1; j < t; j++){
                int z;
                scanf("%d", &z);
                unite(l, z);
            }
        }

        sort(edge, edge + m, cmp);

        printf("%d\n", Kruskal(n));
    }

    return 0;
}
*/
/*
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 505
#define MAXM 25005
int father[MAXN];
int rak[MAXN];
struct Edge{
  int u, v, w;
}edge[MAXM];
int tol;
int cnt;

void addedge(int u, int v, int w)
{
    edge[tol].u = u;
    edge[tol].v = v;
    edge[tol++].w = w;
}

bool cmp(Edge a, Edge b)
{
    return a.w < b.w;
}

void init(int n)
{
    for(int i = 0; i <= n; i++){
        father[i] = i;
        rak[i] = 0;
    }
}

int findfa(int x)
{
    if(father[x] = x) return x;
    else return father[x] = findfa(father[x]);
}

void unite(int x, int y)
{
    x = findfa(x);
    y = findfa(y);

    if(x != y)
        cnt++;

    if(rak[x] < rak[y])
        father[x] = y;
    else{
        father[y] = x;
        if(rak[x] == rak[y])
            rak[x]++;
    }
}

int Kruskal(int n)
{
    sort(edge, edge + tol, cmp);
    int ans = 0;

    for(int i = 0; i < tol; i++){
        int rot1 = findfa(edge[i].u);
        int rot2 = findfa(edge[i].v);
        if(rot1 != rot2){
            ans += edge[i].w;
            unite(rot1, rot2);
        }
        if(cnt == n - 1)
            break;
    }
    if(cnt < n - 1) return -1;
    else return ans;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        tol = 0;

        for(int i = 0; i < m; i++){
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            addedge(u, v, w);
        }

        init(n);
        cnt = 0;
        for(int i = 0; i < k; i++){
            int t, l;
            scanf("%d%d", &t, &l);

            for(int j = 1; j < t; j++){
                int z;
                scanf("%d", &z);
                unite(l, z);
            }
        }

        sort(edge, edge + m, cmp);

        printf("%d\n", Kruskal(n));
    }

    return 0;
}
*/
/*
1
6 4 3
1 4 2
2 6 1
2 3 5
3 4 33
2 1 2
2 1 3
3 4 5 6
*/


四.Legal or Not(HDU3342)

判断是否为DAG, 有dfs和拓扑排序两种写法,我查了个拓扑排序的代码学了一下.

#include <bits/stdc++.h>
using namespace std;
#define N 105

bool G[N][N];
int top[N];///入度

bool topsort(int n)
{
    int cnt = 0;

    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
    {
        if(!top[j]){
            top[j]--;

            cnt++;

            if(cnt == n)
                return true;

            for(int k = 0; k < n; k++){
                if(G[j][k])
                    top[k]--;
            }
        }
    }

    return false;
}

int main()
{
    int n, m;

    while(scanf("%d%d", &n, &m) != EOF && n)
    {
        memset(G, 0, sizeof G);
        memset(top, 0, sizeof top);

        for(int i = 1; i <= m; i++){
            int x, y;
            scanf("%d%d", &x, &y);
            if(!G[x][y]){///防止重复输入导致入度有问题
                G[x][y] = true;
                top[y]++;
            }
        }

        printf(topsort(n)?"YES\n":"NO\n");
    }

    return 0;
}

五.畅通工程续(HDU1874)

最短路问题, 我用Dijkstra写的,套的书上的板子.学习了用vector数组建立图的写法.

注意这是无向图,两个方向都要存.


这个哥们写了四种写法:点击打开链接

里面有SPFA,我没学过,就想着写完了后网上搜博客,更新下我的关于最短路的博客.


#include <bits/stdc++.h>
using namespace std;
#define INF 10000000
#define N 205
#define M 1005

struct edge{int to, cost;};
typedef pair<int, int> P;

vector<edge> G[M];
int d[M];

void dijkstra(int s)
{
    priority_queue<P, vector<P>, greater<P> > que;

    fill(d, d + N, INF);
    d[s] = 0;
    que.push(P(0, s));

    while(!que.empty())
    {
        P p = que.top();que.pop();

        int v = p.second;

        if(d[v] < p.first)
            continue;

        for(int i = 0; i < G[v].size(); i++){
            edge e = G[v][i];

            if(d[e.to] > d[v] + e.cost){
                d[e.to] = d[v] + e.cost;
                que.push(P(d[e.to], e.to));
            }
        }
    }
}

int main()
{
    int n, m;

    while(scanf("%d%d", &n, &m) != EOF)
    {
        for(int i = 0; i < N; i++)
            G[i].clear();

        for(int i = 0; i < m; i++){
            edge temp;
            int u;
            scanf("%d%d%d", &u, &temp.to, &temp.cost);{
                G[u].push_back(temp);
                int uto = temp.to;
                temp.to = u;
                u = uto;
                G[u].push_back(temp);
            }

        }
        int s, e;
        scanf("%d%d", &s, &e);

        dijkstra(s);

        if(d[e] == INF)
            printf("-1\n");
        else
            printf("%d\n", d[e]);
    }

    return 0;
}

六.Eddy's picture(HDU1161)

最小生成树,因为是稠密图所以用Prim写的.(现在百度了一下,都是Prim)

还是用的牛客网的板子.建图是我自己建的.

#include <bits/stdc++.h>
using namespace std;
typedef pair<double, double> P;
#define MAXN 105
#define INF 10000000
bool vis[MAXN];
double lowc[MAXN];
int n;
P pots[MAXN];
double maps[MAXN][MAXN];

double prim()
{
    double ans = 0;
    memset(vis, false, sizeof vis);
    vis[1] = true;

    for(int i = 1; i <= n; i++)
        lowc[i] = maps[1][i];

    for(int i = 2; i <= n; i++){
        double minc = INF;
        int p = -1;
        for(int j = 1; j <= n; j++)
            if(!vis[j] && minc > lowc[j])
        {
            minc = lowc[j];
            p = j;
        }

        if(minc >= INF)
            return -1;

        ans += minc;

        vis[p] = true;

        for(int j = 1; j <= n; j++){
            if(!vis[j] && lowc[j] > maps[p][j])
                lowc[j] = maps[p][j];
        }
    }

    return ans;
}

int main()
{
    while(scanf("%d", &n) != EOF)
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                maps[i][j] = INF;

        for(int i = 1; i <= n; i++){
            double x, y;

            scanf("%lf%lf", &x, &y);
            pots[i].first = x;
            pots[i].second = y;

            for(int j = 1; j < i; j++){
                double dx = fabs(x - pots[j].first);
                double dy = fabs(y - pots[j].second);
                maps[i][j] = maps[j][i] = sqrt(dx*dx + dy*dy);
            }
        }

        printf("%.2lf\n", prim());
    }

    return 0;
}

七.Frogger(POJ2253)

这道题想求最短路中的最长一条路.

链接:点击打开链接

讲解什么的看人家的吧.
我就是这种有点变动就写不下去的菜鸡...
忽略注释掉的Prim........太蠢了
#include <bits/stdc++.h>
using namespace std;
#define N 202
double d[N][N];
typedef pair<int, int> P;
P p[N];

double dis(int i, int j)
{
    double x = fabs(p[i].first - p[j].first);
    double y = fabs(p[i].second - p[j].second);
    return sqrt(x * x + y * y);
}

int main()
{
    int n, Case = 0;

    while(scanf("%d", &n) != EOF && n)
    {
        for(int i = 1; i <= n; i++)
            scanf("%d%d", &p[i].first, &p[i].second);

        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= i; j++)
            d[i][j] = d[j][i] = j == i?0:dis(i, j);

        for(int k = 1; k <= n; k++)
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                d[i][j] = min(d[i][j], max(d[i][k], d[k][j]));

        printf("Scenario #%d\n", ++Case);
        printf("Frog Distance = %.3lf\n\n", d[1][2]);
    }

    return 0;
}

/*
#include <bits/stdc++.h>
using namespace std;
#define INF 100000000
#define N 202
typedef pair<int, int> P;

bool vis[N];
double lowc[N];
double maps[N][N];
P pots[N];
int n;

double prim()
{
    double ans = 0;

    fill(lowc, lowc + N, INF);
    memset(vis, 0, sizeof vis);

    vis[1] = true;
    for(int i = 1; i <= n; i++)
        lowc[i] = maps[1][i];

    for(int i = 2; i <= n; i++){
        int p = -1;
        double minc = INF;

        for(int j = 1; j <= n; j++){
            if(!vis[j] && minc > lowc[j]){
                p = j;
                minc = lowc[j];
            }
        }

        if(p == -1)
            return -1;

        ans = max(ans, minc);
        vis[p] = true;

        for(int j = 1; j <= n; j++){
            if(!vis[j] && lowc[j] > maps[p][j])
                lowc[j] = maps[p][j];
        }
    }

    return ans;
}

int main()
{
    int Case = 0;

    while(scanf("%d", &n) != EOF && n)
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                maps[i][j] = i != j ?INF:0;

        for(int i = 1; i <= n; i++){
            scanf("%d%d", &pots[i].first, &pots[i].second);

            for(int j = 1; j < i; j++){
                double x = abs(pots[i].first - pots[j].first);
                double y = abs(pots[i].second - pots[j].second);
                maps[i][j] = maps[j][i] = sqrt(x*x+y*y);
            }
        }

        printf("Scenario #%d\n", ++Case);
        printf("Frog Distance = %.3lf\n\n", prim());
    }

    return 0;
}
*/


八.产生冠军(HDU2094)

这题是我自己做的(欧耶!), 拓扑排序的题目.
算法很简单,根本不用拓扑排序,直接看入度为0的点是不是只有一个就好了.

#include <bits/stdc++.h>
using namespace std;
#define N 1005

bool G[N][N];
int indeg[N];
int n, k;
string names[N];
/*
bool topsort()
{
    int cnt = 0;

    for(int i = 1; i <= k; i++)
        for(int j = 1; j <= k; j++)
    {
        if(!indeg[j]){
            indeg[j]--;

            {cnt++;cout << i << ' ' << j << ' ' << indeg[j] << endl;}

            if(cnt == k)
                return true;

            for(int l = 1; l <= k; l++){
                if(G[j][l])
                    indeg[l]--;
            }
        }
    }

    return false;
}
*/
int main()
{
    while(scanf("%d", &n) && n)
    {
        memset(G, 0, sizeof G);
        memset(indeg, 0, sizeof indeg);
        for(int j = 1; j <= 2 * n; j++)
            names[j] = "";

        k = 1;
        for(int i = 1; i <= n; i++){
            string a, b;
            cin >> a >> b;
            bool f1 = true, f2 = true;
            int pa, pb;

            for(int j = 1; j < k; j++){
                if(names[j] == a){
                    pa = j;f1 = false;
                }
                if(names[j] == b){
                    pb = j;f2 = false;
                }
            }
            if(f1){
                names[k] = a;
                pa = k;
                k++;
            }
            if(f2){
                names[k] = b;
                pb = k;
                k++;
            }

            if(!G[pa][pb]){
                G[pa][pb] = true;
                indeg[pb]++;
            }
        }

        int cnt = 0;
        for(int i = 1; i < k; i++)
            if(!indeg[i])
                cnt++;

        printf(cnt == 1?"Yes\n":"No\n");
    }

    return 0;
}


九.Ubiquitous Religions(POJ2524)

并查集.每组合一个宗教数量就减一.

#include <bits/stdc++.h>
using namespace std;
#define N 50000

int F[N];
int cnt;

void intl(int n)
{
    for(int i = 1; i <= n; i++)
        F[i] = i;
    cnt = n;
}

int find(int a)
{
    if(F[a] == a)
        return a;
    else
        return F[a] = find(F[a]);
}

void unite(int a, int b)
{
    int x = find(a);
    int y = find(b);

    if(x == y)
        return;
    else{
        F[y] = x;
        cnt--;
    }
}

int main()
{
    int Case = 0;
    int n, m;

    while(scanf("%d%d", &n, &m) != EOF && n)
    {
        intl(n);
        for(int k = 1; k <= m; k++){
            int i, j;
            scanf("%d%d", &i, &j);

            //printf("%d\n", find(8));
            unite(i, j);
        }

        printf("Case %d: %d\n", ++Case, cnt);
    }

    return 0;
}

十.最短路

题目真是取了个赤裸裸的名字.直接套Dijkstra板子就好了

#include <bits/stdc++.h>
using namespace std;
#define INF 100000000
#define N 102
#define M 1002
typedef pair<int, int> P;
struct edge{
    int to, cost;
};
vector<edge> G[N];
int d[M];
int n, m;
int ans = 0;

void dijkstra()
{
    priority_queue<P, vector<P>, greater<P> > que;

    fill(d, d + N, INF);
    d[1] = 0;
    que.push(P(0, 1));

    while(!que.empty())
    {
        P p = que.top();que.pop();

        int v = p.second;

        if(d[v] < p.first)
            continue;

        for(int i = 0; i < G[v].size(); i++){
            edge e = G[v][i];

            if(d[e.to] > d[v] + e.cost){
                d[e.to] = d[v] + e.cost;
                que.push(P(d[e.to], e.to));
            }
        }
    }
}

int main()
{
    while(scanf("%d%d", &n, &m) != EOF && n)
    {
        for(int i = 1; i <= n; i++)
            G[i].clear();

        for(int i = 1; i <= m; i++){
            edge e;
            int u;
            scanf("%d%d%d", &u, &e.to, &e.cost);
            G[u].push_back(e);
            int temp = u;
            u = e.to;
            e.to = temp;
            G[u].push_back(e);
        }

        dijkstra();

        printf("%d\n", d[n]);
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值