HDU1102

入手的图论的第二题。这道题其实挺简单的,就是将已经有路的两个村庄之间的距离处理为0,然后用prim算法即可。

/*************************************************************************
    > File Name: main.cpp
    > Author:Eagles 
    > Mail:None 
    > Created Time: 2018年09月02日 星期日 22时30分07秒
    > Description:HDU1102 
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define N 101
int Grap[N][N];
int dis[N];
int vis[N];
int n;
const int INF=1<<30;

void init()
{
    memset(vis,false,sizeof(vis));
    memset(dis,0,sizeof(dis));

    for (int i=0; i<n; i++)
    {
        for (int j=0; j<n ;j++)
        {
            scanf("%d",&Grap[i][j]);
        }
    }
    int q;
    scanf("%d",&q);

    while (q--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        Grap[a-1][b-1]=Grap[b-1][a-1]=0;
    }

    for (int i=0; i<n; i++)
        dis[i]=Grap[0][i];
    vis[0]=true;
}

void print()
{
    for (int i=0; i<n; i++)
        printf("%d ",vis[i]);
    printf("\n");
    for (int i=0; i<n; i++)
    {
        for (int j=0; j<n; j++)
        {
            printf("%d ",Grap[i][j]);
        }
        printf("\n");
    }
}

void prim()
{
    int sum=0;

    for (int i=0; i<n-1; i++)
    {
        int minn=INF;
        int tmp;

        for (int j=0; j<n; j++)
        {
            if (!vis[j]&&dis[j]<minn)
            {
                minn=dis[j];
                tmp=j;
            }
        }

        vis[tmp]=true;
        sum+=minn;

        for (int j=0; j<n; j++)
        {
            if (!vis[j]&&Grap[tmp][j]<dis[j])
                dis[j]=Grap[tmp][j];
        }
    }
    printf("%d\n",sum);
}

int main()
{
    while (~scanf("%d",&n))
    {

        init();
        //print();
       prim();
    }
    return 0;
}

但是这是我看了别人的题解之后做的。我原来可不是这样做的,所以一直WA,通过随机生成测试数据与别人程序对拍之后发现了问题所在。我原来的思路是:首先建立一个con[][]数组,用来保存已经有路连接的村庄,如果有路,con[i][j]=1,反之则为0.当处理完成后,由于我是选的第一个村庄作为prim算法的起始点,所以就将与第一个村庄相连的所有村庄加入到已经访问过的集合当中,但要注意的是,在加入的过程中要更新dis[]数组,即访问的集合到未访问的集合的各点的最小距离。怎样确定哪些村庄与第一个村庄相连呢?举个例子,比如1跟2相连,2跟3,4,6相连,那么1就跟2,3,4,6都相连,所以con[0][1],con[0][2],con[0][3],con[0][5]都应该为1.这里我用的是dfs来处理.原以为这样提交后可以AC,但是结果给我泼了一盆冷水。我苦思冥想了一个晚上,今天早上再测试了一下,才发现了问题所在:如果是1跟2相连,2跟3,4,6相连,那么比如5跟7相连但是不和1相连,那我上面的处理就没有利用到这一个语句,所以导致了错误。我在代码中改了一下,结果AC了,其实也就是如果相连,那么距离为0,跟别人的一样,但总算还是发现了错误。
WA,修改之后AC的代码:


/*************************************************************************
    > File Name: main.cpp
    > Author:Eagles 
    > Mail:None 
    > Created Time: 2018年09月02日 星期日 16时21分42秒
    > Description:HDU1102 
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int INF=1<<30;
#define N 101
int Gra[N][N];
int con[N][N];
bool vis[N];
int dis[N];
bool used[N];
int n;

void init()
{
    memset(vis,false,sizeof(vis));
    memset(con,0,sizeof(con));
    memset(used,false,sizeof(used));

    for (int i=0; i<n; i++)
    {
        for (int j=0; j<n; j++)
        {
            scanf("%d",&Gra[i][j]);
            if (i==j)
                Gra[i][j]=0;
        }
    }

    for (int i=0; i<n; i++)
        dis[i]=Gra[0][i];

    int q;
    scanf("%d",&q);

    while (q--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        con[a-1][b-1]=1;
        con[b-1][a-1]=1;
    }
}

void dfs(int i)
{
    if (used[i])
        return;
    int flag=0;

    for (int j=0; j<n; j++)
    {
        if (con[i][j])
        {
            flag=1;
        }
    }


    if (flag==0)
        return;

    used[i]=true;

    for (int j=1; j<n; j++)
    {
        if (con[i][j]==1)
        {

            con[0][j]=1;
            dfs(j);
        }
    }

}

void ddfs(int i)//将不与第一个村庄相连,但是与其他相连的村庄的距离变为0
{
    if (used[i])
        return ;

    used[i]=true;

    for (int j=0; j<n; j++)
    {

        if (con[i][j]==1)
        {
            Gra[i][j]=Gra[j][i]=0;
            ddfs(j);

        }
    }
}

void prim()
{

    dfs(0);
    con[0][0]=0;
    for (int i=1; i<n; i++)//不与第一个相连,但是与其他相连,距离处理为0
    {
        if (con[0][i]==0)
            ddfs(i);

    }    

    vis[0]=true;
    int cnt=1;

   for (int i=0; i<n; i++)
    {
        if (con[0][i]==1)
        {
            cnt++;
            vis[i]=true;

            for (int j=0; j<n; j++)
            {
                if (!vis[j]&&Gra[i][j]<dis[j])
                    dis[j]=Gra[i][j];
            }
        }
    }

    int sum=0;

    for (int i=0; i<n-cnt; i++)
    {
        int minn=INF;
        int tmp;

        for (int j=0; j<n; j++)
        {
            if (!vis[j]&&dis[j]<minn)
            {
                minn=dis[j];
                tmp=j;
            }
        }

        sum+=minn;
        vis[tmp]=true;

        //printf("sum:%d,tmp:%d\n",sum,tmp);
        for (int j=0; j<n; j++)
        {
            if (!vis[j]&&Gra[tmp][j]<dis[j])
                dis[j]=Gra[tmp][j];
        }

    }
    printf("%d\n",sum);
}
void print()
{
    for (int i=0; i<n; i++)
    {
        for (int j=0; j<n; j++)
        {
            printf("%d ",Gra[i][j]);
        }
        printf("\n");
    }
}
int main()
{

    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);

   while (~scanf("%d",&n))
    {

        init();
        prim();
        //print();
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值