图论day1(田地上的环/骑马修栅栏/热浪/dijkstra模版)

可以说是很绝望的一天了,把图的基本知识&dfs遍历bfs遍历&floyed&dijkstra过了一遍,沉浸在“听的都会,做的都错”的玄妙阶段。
基础这一块,邻接表还是非常非常不熟,平时练习不妨多用用。这种理解比较困难的东西,这段时间一定要记得按时巩固啊,理解透了会好很多吧……。



luogu10767田地上的环。

FJ 让他的N (1 <= N <= 250)只编号为从1到N的奶牛在田地里玩.这些奶牛决定用M条(1<=M<=N*(N+1)/2)牛绳将各自连接起来.当然,不会出现一对牛被两条及以上牛绳连接起来.输入告诉你每一条牛绳连接的两只奶牛C1和C2(1 <= c1 <= N; 1 <= c2 <= N; c1 <> c2).
FJ要求奶牛们与1号奶牛相连.现在你要帮助FJ找出所有没有与1号奶牛相连的奶牛.这里的相连既可以是直接的,也可以是间接的(特别的,1号奶牛总是与自己相连).将没有与1号奶牛相连的奶牛的编号升序输出.如果你找不到这样的一只牛,那么就输出0.

是道水题,但是注释敲得很认真,还是贴上来吧……而且用邻接表没过,明天再试试。
(明天:过了!!!是因为把赋值打成==了………………而且1号是一定与1号连的,所以1不能输出。)

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[300][300]={};//邻接矩阵存图,0表示无边,1有边。 
int b[300]={};//判断该数是否搜索过,0表示未搜索,1表示已搜。 

void dfs(int x)
{
    b[x]=1;//当前点标记为搜索过。 
    for(int j=1;j<=n;j++) 
    {
        if(b[j]!=1&&a[x][j]==1) dfs(j);//如果点j未搜过且点x-点j有边,搜索。 
    }
    return;
}//深搜。 

int main()
{
    cin>>n>>m;
    int x,y;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y;
        a[x][y]=1;
        a[y][x]=1;//读入绳子两头连着的牛,因为是无向图,所以 a[x][y]和a[y][x]都有边。 
    }
    dfs(1);//题目只要求输出未与1号相连的,所以只要从1开始搜索,标记所有能搜到的点,剩下仍为0的就是未搜过的,即与1号不相连的。 
    int flag=0; //如果最后仍=0,就说明没有点不与1号相连,按题目要求输出0. 
    for(int i=2;i<=n;i++)
    {
        if(b[i]==0) cout<<i<<endl,flag=1;//如果有点与1号不相连,就输出,并且flag改为1. 
    }
    if(flag==0) cout<<"0";
    return 0;
}



luogu2731.骑马修栅栏。

John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。
每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(>=1)个栅栏。两顶点间可能有多个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。
你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一位较小的,如果还有多组解,输出第二位较小的,等等)。

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

int n;
int a[600][600]={};//存边。 
int du[600]={};//存度数。 
int c[2500]={}; //数组开大一点啊啊啊啊啊啊啊啊啊,边数和点数最大是平方关系啊!!! 
int jishu=0;
int s=0;//点的最大编号。

void dfs(int x)
{
    for(int i=1;i<=s;i++)
    {
        if(a[x][i]!=0) 
        {
            a[x][i]--;a[i][x]--;//相应的边--,表示走过一遍。
            dfs(i);
        }
    }
    c[++jishu]=x;//存路径是搜完再存的的,所以放在函数开头会gg。
    return;
}

int main()
{
    cin>>n; 
    for(int i=1;i<=n;i++)
    {
        int x,y;
        cin>>x>>y;
        a[x][y]++;a[y][x]++;//无向图。两条边计数++。
        du[x]++;du[y]++;//相应点的度数++。
        s=max(s,max(x,y));//求点的最大编号。
    } 
    int Num=0;//存奇点编号。 
    for(int i=1;i<=s;i++)
    {
        if(du[i]%2==1){
            Num=i;break;
        }
    } //找最小奇点。 
    if(Num==0)
    for(int i=1;i<=s;i++)
        if(du[i]!=0) 
        {
            Num=i;break;
        }//如果没有奇点,找最小的有边的点。
    dfs(Num); //深搜。
    for(int i=jishu;i>=1;i--) cout<<c[i]<<endl;//倒序输出。
    return 0;
} 

据说是典型的欧拉模版题,关于这个问题是否有解、如何求解的方法在另一篇博客《叽里呱啦》里。


luogu1339.热浪。

FJ已经研究过可以把牛奶从威斯康星运送到德克萨斯州的路线。这些路线包括起始点和终点先一共经过T (1 <= T <= 2,500)个城镇,方便地标号為1到T。除了起点和终点外地每个城镇由两条双向道路连向至少两个其它地城镇。每条道路有一个通过费用(包括油费,过路费等等)。
给定一个地图,包含C (1 <= C <= 6,200)条直接连接2个城镇的道路。每条道路由道路的起点Rs,终点Re (1 <= Rs <= T; 1 <= Re <= T),和花费(1 <= Ci <= 1,000)组成。求从起始的城镇Ts (1 <= Ts <= T)到终点的城镇Te(1 <= Te <= T)最小的总费用。

用dijkstra过的。感觉上是模版题,做了几道前面的不考虑权值的题,做到这道,一开始居然产生了“最短路万一不是最便宜的怎么办”这种愚蠢的念头。

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

int t,c,ts,te;
int a[2500][2500];//存图。 
int dis[2500]={};//存最短路径。
int flag[2500]={};//1表示该点是最优,0则不是。 

void dijkstra()
{
    for(int i=1;i<=t;i++) dis[i]=a[ts][i];  //初值,即起点到各点的距离。 
    dis[ts]=0;flag[ts]=1;//起点到起点距离为零,已是最优。
    for(int i=1;i<=t;i++)
    {
        int minn=10000000;
        int k=0;//该点连接的最短边终点编号。 
        for(int j=1;j<=t;j++)
        {
            if(flag[j]==0&&dis[j]<minn) minn=dis[j],k=j; 
        }//求出当前起点到各点的最小值。
        if(k==0) return;//这句话只针对非联通图……,此处无意义。
        flag[k]=1;//当前点已最优。
        for(int j=1;j<=t;j++) 
        {
            if(flag[j]==0&&dis[k]+a[k][j]<dis[j]) dis[j]=dis[k]+a[k][j];//如果起点到k距离+k点到j点距离已小于原本存的路径长,则更新。
        }
    }
    return;
}

int main()
{
    scanf("%d%d%d%d",&t,&c,&ts,&te);
    memset(a,10,sizeof(a));//给各条边赋初值,不能赋零,会使最短路计算出错。没边应该赋无限大,就不会干扰最短路径的计算了。
    for(int i=1;i<=ts;i++) a[i][i]=0;//第i个点到第i个点,路径=0.
    for(int i=1;i<=c;i++)
    {
        int x,y,v;//起点、终点、权值。
        scanf("%d%d%d",&x,&y,&v);
        if(v<a[x][y]) a[x][y]=v,a[y][x]=v;//读入时,由于每两个点中间可能会有两条以上的路,所以要选最小的存。注意是无向图。
    }
    dijkstra();
    cout<<dis[te];
    return 0;
} 

dijkstra本质上还是贪心的思想,每一步都取最优,从而达成整体的最优。显而易见。……

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值