信息学奥赛一本通(C++版) 第三部分 数据结构 第四章 图论算法

总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716

信息学奥赛一本通(C++版) 第三部分 数据结构   第四章 图论算法

http://ybt.ssoier.cn:8088/

第一节 图的遍历
//1341 【例题】一笔画问题
//在想,是输出欧拉路,还是欧拉回路
//从哪点开始遍历,
//点的数据范围,边的数据范围
//欧拉路的理解,经过所有点,欧拉回路的理解,某点出发,最后回到某点,中间经过其他所有的点
//一直很困后,欧拉路,还是欧拉回路,突然想到在欧拉路里加一层判断,进一步判断是否有欧拉回路
//从数据输出来看,该题是个无向图,准备用邻接矩阵来存储数据
//提交,两个结果,不是答案错误,就是运行超时
//欧拉回路
//看了此文,深受启发https://www.cnblogs.com/sssy/p/6682871.html?utm_source=itdadao&utm_medium=referral
//大幅修改代码
//提交,没有超时了,不过,全是答案错误,
//参考http://www.cnblogs.com/zhourongqing/archive/2012/09/18/2690185.html 提供一组测试数据
//输入:
//6 9
//1 2
//2 3
//3 4
//4 2
//4 5
//2 5
//5 6
//4 6
//输出:
//5 6 4 5 2 4 3 2 1
//明白了,自己对欧拉路或回路的误解,并不是每个点只通过一次,而是每条边最多通过一次,将所有点都遍历了,
//具体到每个点,可以通过多次,那么该题的核心是对边的处理
//参考了https://wenku.baidu.com/view/f533dc147c1cfad6185fa7bb.html写法 调试程序过程中,发现该写法是错误的
//程序继续大改,样例继续通过,提交AC 2017-11-11 22:46
//欧拉算法与回溯算法很不同。 

//成熟的方法 2017-11-13 18:48
#include <stdio.h>
#include <string.h>
int a[510][510],b[1100],du[510],n,cnt=0;//b存路径
void dfs(int i){
    int j;
    for(j=1;j<=n;j++)
        if(a[i][j]){
            a[i][j]=0,a[j][i]=0;
            dfs(j);
        }
    b[cnt++]=i;

int main(){
    int m,i,j,u,v,count=0,start;
    memset(a,0,sizeof(a)),memset(du,0,sizeof(du));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        a[u][v]=1,a[v][u]=1,du[u]++,du[v]++;
    }
    start=1;//起点,欧拉回路 
    for(i=1;i<=n;i++)
        if(du[i]%2==1){
            count++;
            if(count==1)
                start=i;//欧拉路径 
        }
    dfs(start);
    for(i=cnt-1;i>=0;i--)printf("%d ",b[i]);
    return 0;

不成熟的方法:
#include <stdio.h>
#include <string.h>
int a[1000][1000],n,m,d[1000],b[1000],count=0;
void dfs(int i){//欧拉路
    int j;
    for(j=1;j<=n;j++){
        if(a[i][j]==1){
            a[i][j]=0,a[j][i]=0;
            dfs(j);
            b[count++]=j;
        }
    }
    
}
int main(){
    int i,j,u,v,cnt=0;
    memset(a,0,sizeof(a));
    memset(d,0,sizeof(d));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){//此处写成 for(i=1;i<=n;i++)大失水准
        scanf("%d%d",&u,&v);
        a[u][v]=1,a[v][u]=1;
        d[u]++,d[v]++;
    }
    u=1;
    for(i=1;i<=n;i++)
        if(d[i]%2==1){
            cnt++;
            if(cnt==1)
                u=i;
        }
    dfs(u);
    for(i=0;i<count;i++)
        printf("%d ",b[i]);
    printf("%d",u);
    return 0;
}

 

//1374 铲雪车(snow)
//看完题目后,明显感觉题目的样例输出部分有问题
//搜索网络,https://www.cnblogs.com/olahiuj/p/5781343.html给出详细输入输出数据,经确认与该题不符
//继续搜索,http://www.oier.cc/ssoj2426%E9%93%B2%E9%9B%AA%E8%BD%A6%E9%97%AE%E9%A2%98/样例与该题相符 ,摘抄如下
//输入:
//0 0
//0 0 10000 10000
//5000 -10000 5000 10000
//5000 10000 10000 10000
//输出:
//3:55
//思路也摘抄如下:
//每一条路都只铲雪一遍,这样是最节省时间的;因为是双向道路,
//所以可以从任何一个点走一圈回到起点,这就是欧拉回路。知道路程和速度,
//就可以快速求出时间,输出时注意时间的格式以及精确到分钟需要四舍五入就行。
//(平方可能会超过int,建议使用long long)
//不得不承认,上述在该题写的代码相当棒,美中不足就是代码是图片版本的。
//本人对该题看法:
//该题,没说明那些道路有雪,那些道路无雪,从解题的情况来看,是每个道路都有雪,并且是两个车道都有雪
//AC 2017-11-12 9:26
#include <stdio.h>
#include <math.h>
int main(){
    long long s,e,x1,y1,x2,y2,h,m;
    double d=0,ans;
    scanf("%lld%lld",&s,&e);//这句只是为了读取,在题中数据无用,因是欧拉回路,故算总路程,哪点出发都行
    while(scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2)!=EOF)
        d+=sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));//此处写成d=sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));//若用int 该题数据会溢出
    ans=d*2/1000/20;//此处写成ans=d/1000/20;//2两倍距离1000化成km,20对应小时
    h=(long long)ans;
    m=(long long)((ans-h)*60+0.5);//0.5四舍五入
    printf("%lld:%02lld",h,m);//还要注意时间的输出格式,注意补零 ,该题要做对很不容易
    return 0;
}
//洛谷 P2731 骑马修栅栏 Riding the Fences
//1375 骑马修栅栏(fence)
//开始以为栅栏对应点,以为该题是哈密尔顿回路,整道题读完,发现栅栏对应边,该题是欧拉路径
//该题难点,总的点的个数没有说明,看了一眼https://www.cnblogs.com/gzhonghui/p/5707943.html代码
//需区分欧拉路径,欧拉回路
//样例通过,提交,测试点6,7,8答案错误
//重新看了一遍https://www.cnblogs.com/gzhonghui/p/5707943.html代码,发现最小的点竟然可能不是1,也得自个求
//修改,提交,测试点6,7,8答案错误
//搜索网络,https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P2731
//提到,两顶点间可能有多个栅栏,于是我们就要统计2个点之间的栅栏条数(好坑啊)
//马上修改,提交,还是错了测试点6,7,8
//下载测试点6的数据,才发现,问题是意识到了,但读取数据的时候没有做边的自增修改,
//修改,提交,发现错了测试点7,8
//继续下载测试点7的数据,
//仿照他人代码就行修改,提交AC 2017-11-12 19:14
//一个疑问,记录路径时,放在dfs()之前,与放在循环结束之后,为什么会有偏差,函待解决。
#include <stdio.h>
#include <string.h>
int a[510][510],b[2000],begin=999999,n=0,du[510],cnt_b=0;//b[]记录走过的点,注意点可以重复,注意b要开得大些 n点的数目
int max(int a,int b){
    return a>b?a:b;
}
int min(int a,int b){
    return a<b?a:b;
}
void dfs(int i){
    int j;
    for(j=begin;j<=n;j++)//此处写成 for(j=1;j<=n;j++)
        if(a[i][j]){
            a[i][j]--,a[j][i]--;//此处写成 a[i][j]=0,a[j][i]=0;
            //b[cnt_b++]=j;//为了解决测试点7,8
            dfs(j);
        }
    b[cnt_b++]=i;//为了解决测试点7,8
}
int main(){
    int m,i,j,u,v,cnt_du=0,start;
    memset(a,0,sizeof(a)),memset(du,0,sizeof(du));
    scanf("%d",&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&u,&v);//无向图
        a[u][v]++,a[v][u]++,du[u]++,du[v]++;//此处写成 a[u][v]=1,a[v][u]=1,du[u]++,du[v]++;
        n=max(n,max(u,v)); //参考他人代码,才想到的
        begin=min(begin,min(u,v));
    }
    start=begin;//此处写成start=1;//欧拉回路,从第1个点开始
    for(i=begin;i<=n;i++)//此处写成 for(i=1;i<=n;i++)
        if(du[i]%2==1){
            cnt_du++;
            if(cnt_du==1)start=i;//欧拉路径,从第i个点开始
        }
    dfs(start);
    //printf("%d\n",start);//为了解决测试点7,8
    //for(i=0;i<cnt_b;i++)printf("%d\n",b[i]);//为了解决测试点7,8
    for(i=cnt_b-1;i>=0;i--)printf("%d\n",b[i]);//为了解决测试点7,8
    return 0;
}
 
2017-11-12 20:50 AC该节内容
第二节 最短路径算法
//1342 【例4-1】最短路径问题
//因n<=100,可以考虑Floyd算法
//样例很快通过,提交AC 2017-11-12 23:01
#include <stdio.h>
#include <math.h>
#define INF 1999999999
double d[110][110];
long long x[110],y[110];
int main(){
    int n,m,i,j,k,u,v;
    double s;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]);
    scanf("%d",&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            d[i][j]=d[j][i]=INF;
    for(i=1;i<=n;i++)d[i][i]=0;
    for(i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        s=sqrt((x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v]));
        d[u][v]=s,d[v][u]=s;//无向图
    }
    for(k=1;k<=n;k++)//以某个点为中转,整个邻接边重新算一遍,故k打头
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(d[i][j]>d[i][k]+d[k][j])
                    d[i][j]=d[i][k]+d[k][j];
    scanf("%d%d",&u,&v);
    printf("%.2lf",d[u][v]);
    return 0;
}
 

 

//1342 【例4-1】最短路径问题
//n<=100 考虑用Floyd算法,简单,方便,快捷
//样例通过,提交AC 2017-11-27
#include <stdio.h>
#include <math.h>
#define INF 999999999
struct node{
    int x,y;
}p[110];
double a[110][110],d;
int main(){
    int i,j,k,n,m,u,v,x1,y1,x2,y2,s,t;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d%d",&p[i].x,&p[i].y);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(i==j)a[i][j]=0;
            else a[i][j]=INF;
    scanf("%d",&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        x1=p[u].x,y1=p[u].y,x2=p[v].x,y2=p[v].y;
        d=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
        a[u][v]=d,a[v][u]=d;//无向图
    }
    scanf("%d%d",&s,&t);
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(a[i][j]>a[i][k]+a[k][j])
                    a[i][j]=a[i][k]+a[k][j];
    printf("%.2lf",a[s][t]);
    return 0;
}

 

//1343 【例4-2】牛的旅行

//P1522 牛的旅行 Cow Tours

http://blog.csdn.net/mrcrack/article/details/70112975

//首先说明一点,翻译得很不好,还是看原版的比较好。
//http://blog.csdn.net/supersnow0622/article/details/9763979此文写得不错,摘抄如下:
//分析:首先要连在一起修路的这两个牧区,之前是不连通的。其次就是这两个牧区修路之后,
//相隔最远的两个牧区的距离最小。其实,只要枚举任意不连通的两个牧区在计算这种情况下的最远的距离就好了,
//然后在这些最远的距离中找到距离最小(题目要求半径最小)的一个就是所求了。
//关键是在两个不连通的牧区修路之后求牧场的半径呢?假设已将one牧区种的D和two牧区中的F相连,
//想一下,这个牧场的半径是不是等于与D相距最远的点到D的距离+D与F的距离+与F相距最远的点的距离呢?
//这样的求法是满足题目所说的牧场的半径是相距最远的两个点的距离。
//所以思路很快就出来了:
//1.用floyed计算任意两点间的距离
//2.计算每个点与其他点的最远距离
//3.计算任意两个不连通的点修路之后牧场的半径。
//http://www.xuebuyuan.com/1457314.html此文给出了英文原版题目,读下来,配合中文意思,弄懂题目了。
//http://www.nocow.cn/index.php/USACO/cowtour根据该文,弄懂了,为什么
//那我们只需要计算新连成的区块的直径,然后与原来的所有区块的直径放在一起,取最大直径就可以了
//突然对为什么取最大直径,又心存疑虑,想了3个小时,想不通,暂时搁置吧。
//老外的题,与中国的题,确实存在着文化差异。感叹。
//编码的过程中,真是服了,洛谷的样例输入数据有问题。查找了一通,发现没有问题,数据格式本就是如此。折腾了一场。
#include <stdio.h>
#include <string.h>
#include <math.h>//洛谷中,该头文件,必须以C++形式提交。
#define INF 1000000
int x[150+10],y[150+10];
double a[150+10][150+10],d[150+10],minD=INF;
char s[200];
double dis(int x1,int y1,int x2,int y2){
    return sqrt((double)(x1-x2)*(x1-x2)+(double)(y1-y2)*(y1-y2));//别忘了强转,否者容易溢出100000*100000
}
int main(){
    int i,j,k,n,w;
    memset(d,0,sizeof(d));
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d%d",&x[i],&y[i]);
    for(i=1;i<=n;i++){
        scanf("%s",s+1);
        for(j=1;j<=n;j++){
            if(i!=j){
                if(s[j]=='0')
                    a[i][j]=a[i][j]=INF;
                else{
                    a[i][j]=a[j][i]=dis(x[i],y[i],x[j],y[j]);
                }
            }else
                a[i][j]=0;
        }
    }
    for(k=1;k<=n;k++)//floyd算法
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(a[i][j]>a[i][k]+a[k][j])
                    a[i][j]=a[i][k]+a[k][j];
    for(i=1;i<=n;i++)//连通区块里找最大值
        for(j=1;j<=n;j++)
            if(a[i][j]<INF&&a[i][j]>d[i])
                d[i]=a[i][j];                
    for(i=1;i<=n;i++)//连接两个牧区,找最小值。
        for(j=1;j<=n;j++)
            if(a[i][j]==INF&&minD>d[i]+d[j]+dis(x[i],y[i],x[j],y[j]))
                minD=d[i]+d[j]+dis(x[i],y[i],x[j],y[j]);
    for(i=1;i<=n;i++)//此处想不明白,为什么找最大值。 等水平高了再回看此题。2017-6-20 21:23
        if(minD<d[i])
            minD=d[i];
    printf("%.6lf\n",minD);//1此处写成printf("%d\n",minD);低级错误
    return 0;
}

//1344 【例4-4】最小花费
//从题目构成看,是求最短路径
//1<=n<=2000 Floyd算法用不了,上Dijkstra算法
//因数据范围限制,不采用邻接矩阵,采用邻接表的方式存储图数据
//但转念一想,该题没给边的数据范围,故还是采用邻接矩阵进行存储
//在测试样例的过程中,发现 样例 输入 输出竟然 没弄懂。
//1->3 3 100/(1-0.03)=103.09278350515463917525773195876
//1->2 2->3 100/((1-0.01)(1-0.02))= 103.09278350515463917525773195876
//写到此刻,意识到,该题挺难的。
//暂时搁置2017-11-27
//http://blog.sina.com.cn/s/blog_4f3b79d00100aqi7.html此文介绍得不错
//【试题解析】
//本题其实是求从A到B的最短路径问题(可用Dijstra或SPFA算法),解题的关键是构图:
//以人员编号为顶点,若x号人和y号人之间可以相互转账则在x和y之间连一条边,边的权为100/(100-z)。对于样例构图如下:

 
//最小花费——一道最短路径试题
//因为B到帐后收到100元,所以,原题等价于求从B到A的最短路径(权值相乘),由于乘法满足交换律,故也可求从A到B的最短路径。对于样例,从A到B的最短路径为:1→2→3,权值为:[100/(100-1)]*[100/(100-2)]= 1.0307153164,最后的结果为:103. 07153164(保留小数点后8位)。
//算法是与一般的最短路径不同,
//样例通过,提交,测试点,答案全部错误,一查,漏了二维数组的初始化,添加,提交AC, 2017-12-1
#include <stdio.h>
#include <string.h>
#define INF 999999999
int n,m,vis[2010],A,B;
double d[2010],a[2010][2010];
int findMin(){
    int i,k;
    double min=999999999;
    for(i=1;i<=n;i++)
        if(vis[i]==0&&d[i]<min)
            min=d[i],k=i;
    return k;
}
void Dijkstra(){//剩下n-1个点
    int cnt=1,k,i;
    while(cnt<n){//此处写成 cnt<n 花了点时间
        cnt++;
        k=findMin();
        vis[k]=1;
        for(i=1;i<=n;i++)
            if(vis[i]==0&&d[i]>d[k]*a[k][i])//以k为中转
                d[i]=d[k]*a[k][i];
    }
}
int main(){
    int i,j,u,v,w;
    memset(a,0,sizeof(a));memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)//漏了初始化,提交答案全部错误
        for(j=1;j<=n;j++)
            if(i==j)a[i][j]=0;
            else a[i][j]=INF;
    for(i=1;i<=m;i++)scanf("%d%d%d",&u,&v,&w),a[u][v]=a[v][u]=100.0/(100-w);//此处不能写成a[u][v]=a[v][u]=100.0*100/(100-w);仔细想过,确实错的//无向图
    scanf("%d%d",&A,&B);
    vis[A]=1;
    for(i=1;i<=n;i++)
        if(a[A][i])
            d[i]=a[A][i];
    Dijkstra();
    printf("%.8lf",d[B]*100);
    return 0;
}
//1345 【例4-6】香甜的黄油
//若用Floyd算法,该题很快能解决,无奈牧场数P(2≤P≤800)超时是必然
//准备采用SPFA算法,但对超时还是有顾忌,算法复杂度 N*P*C
//准备翻看他人代码,但转念一想,还是要验证自己的思路
//先自个编。
//翻看了http://blog.csdn.net/loi_imcy/article/details/49405447
//https://www.cnblogs.com/zbtrs/p/5947209.html
//两篇文章,发现,想是想到了,就欠缺一步编码,可惜了。
//样例通过,提交AC 2017-12-24 16:49
//翻看 https://www.luogu.org/problemnew/solution/P1828
//SPFA算法,O(kE) E是边数,k是常数,平均值为2。Bellman-Ford算法的一种队列实现
//该题,算法复杂度O(k*E*p)
#include <stdio.h>
#include <string.h>
#define INF 999999999
int n,p,c,head[810],cnt=0,d[810],q[10*810],vis[810],a[510];
struct node{
    int to,next,w;
}e[1500*2];//无向图
void SPFA(int s){
    int u,v,b,h,t,i,w;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=p;i++)d[i]=INF;
    d[s]=0,h=t=1,q[t]=s,t++,vis[s]=1;
    while(h<t){
        u=q[h];
        for(b=head[u];b;b=e[b].next){
            v=e[b].to,w=e[b].w;
            if(d[v]>d[u]+w){
                d[v]=d[u]+w;
                if(vis[v]==0)q[t]=v,t++,vis[v]=1;
            }
        }
        vis[u]=0;
        h++;
    }
}
void add_edge(int u,int v,int w){
    cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;
}
int main(){
    int i,j,u,v,w,ans=INF,sum;
    memset(head,0,sizeof(head));
    scanf("%d%d%d",&n,&p,&c);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    for(i=1;i<=c;i++){
        scanf("%d%d%d",&u,&v,&w);
        add_edge(u,v,w),add_edge(v,u,w);
    }
    for(i=1;i<=p;i++){
        SPFA(i),sum=0;
        for(j=1;j<=n;j++)sum+=d[a[j]];
        ans=ans>sum?sum:ans;
    }
    printf("%d",ans);
    return 0;
}


 

//1376 信使(msner)
//1<=n<=100采用floyd算法,简单,易写
//在最短路径中找最大值,既为最短时间,请注意,起点是指挥所1,终点是剩下的n-1个点
//样例通过,提交AC 2017-11-30
#include <stdio.h>
#define INF 999999999
int a[110][110];
int main(){
    int i,j,k,n,m,max=-999999999,u,v,w;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(i==j)a[i][j]=0;
            else a[i][j]=INF;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        a[u][v]=w,a[v][u]=w;//无向图
    }
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(a[i][j]>a[i][k]+a[k][j])
                    a[i][j]=a[i][k]+a[k][j];
    for(j=1;j<=n;j++)
        if(a[1][j]>max)max=a[1][j];
    if(max==INF)printf("-1");
    else printf("%d",max);
    return 0;
}

//1377 最优乘车(travel)
//怀疑该题的输入数据有误
//翻看http://codevs.cn/problem/1722/发现是同一道题
//输入数据如下:
//3 7
//6 7
//4 7 3 6
//2 1 3 5
//http://blog.csdn.net/u011434547/article/details/37925685通过此文基本弄明白了数据的读取以及处理
//思路摘抄如下:
//主要的还是对数据的处理,一个巴士线路上的站都只要乘一次就到了,那么我们建立图,
//如果i站点和j站点同时出现在一个巴士线路上就连一条边,他们之间的权值为1.
//然后用最短路处理,dijkstra和floyed都能处理
//为了方便读取,采用C++方式编写
//样例通过,提交,测试点1-3,6-8,10答案错误
//scanf("%d%d\n",&m,&n);//请注意此处写法,scanf("%d%d\n",&m,&n);原来写成 scanf("%d%d",&m,&n);getchar();测试点1-3,6-8,10答案错误
//提交AC 2017-12-24 21:46 该题很大一部分难在数据读取。
//该题是有向图
#include <iostream>
#include <sstream>
#include <string>
#include <cstdio>
using namespace std;
#define INF 999999999//此处写成 999999999
int num[510],a[510][510];
int main(){
    int m,n,i,j,k,len,x;
    string str;
    scanf("%d%d\n",&m,&n);//请注意此处写法,scanf("%d%d\n",&m,&n);原来写成 scanf("%d%d",&m,&n);getchar();测试点1-3,6-8,10答案错误
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(i==j)a[i][j]=0;
            else a[i][j]=INF;
    for(k=1;k<=m;k++){//以下读取手法,请注意学习
        getline(cin,str);
        stringstream in(str);
        len=0;
        while(in>>x)num[++len]=x;
        for(i=1;i<=len;i++)
            for(j=i+1;j<=len;j++)
                a[num[i]][num[j]]=1;
    }
    for(k=1;k<=n;k++)//此处写成 for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)//此处写成for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)//此处写成 for(j=1;j<=n;j++)
                if(a[i][j]>a[i][k]+a[k][j])
                    a[i][j]=a[i][k]+a[k][j];
    if(a[1][n]==INF)printf("NO");//此处写成 if(a[1][n]==INF)
    else printf("%d",a[1][n]-1);//此处写成  else printf("%d",a[1][n]-1);
    return 0;
}
 

 

//1378 最短路径(shopth)
//n<=80采用Floyd算法
//该题,数据读取遇到了极大的障碍
//翻看http://www.oier.cc/ssoj2433%e6%9c%80%e7%9f%ad%e8%b7%af%e5%be%84shopth/代码,读取数据受到启发
//样例通过,提交AC,Floyd算法真牛,能处理负边。2017-12-13  
#include <stdio.h>
#define INF 999999999
int a[100][100];
int main(){
    int n,s,i,j,b,k;
    scanf("%d%d",&n,&s);
    for(i=1;i<=n;i++){
        for(j=1;j<=n;j++){
            if(scanf("%d",&b)==1)a[i][j]=b;//数据读取技巧
            else a[i][j]=INF;
        }
    }
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(a[i][j]>a[i][k]+a[k][j])a[i][j]=a[i][k]+a[k][j];
    for(j=1;j<=n;j++)
        if(j!=s)printf("(%d -> %d) = %d\n",s,j,a[s][j]);
    return 0;
}


 

//1379 热浪(heatwv)

1.//P1339 [USACO09OCT]热浪Heat Wave

http://blog.csdn.net/mrcrack/article/details/70112975

//无向图
//邻接矩阵2500*2500占空间有点多,准备尝试邻接表
//Dijkstra 邻接表的写法,这篇文章写得不错 https://www.douban.com/note/296102336/
//很快编写好,样例通过,提交AC,一次性成功,代码写下来,竟然不用调试,难得。2017-6-16 21:26
//程序效率:耗时:159ms 内存:8535kb
#include <stdio.h>
#include <string.h>
#define INF 9999
struct node{
    int to,next,w;
}e[6200*2+100];
int head[2500+100],cnt=0,vis[2500+100],d[2500+100];
void add(int u,int v,int w){//无向图
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt,e[cnt].w=w;//从头插入,head[u]指向节点u对应的边,cnt边,e[cnt].to,cnt边对应的终点,e[cnt].w,cnt边对应的权重w
    cnt++,e[cnt].to=u,e[cnt].next=head[v],head[v]=cnt,e[cnt].w=w;
}
int main(){
    int t,c,ts,te,i,j,k,u,v,w,min;
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
    scanf("%d%d%d%d",&t,&c,&ts,&te);
    for(i=1;i<=c;i++){
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for(i=1;i<=t;i++)
        d[i]=INF;
    d[ts]=0;
    for(i=1;i<=t;i++){//剩下t个点
        u=0,min=INF;
        for(j=1;j<=t;j++)
            if(vis[j]==0&&d[j]<min){//找d[j]中的最小值
                min=d[j];
                u=j;
            }
        vis[u]=1;
        k=head[u];//k是边
        while(k!=0){//邻接表处理
            v=e[k].to;
            if(vis[v]==0&&d[u]+e[k].w<d[v])
                d[v]=d[u]+e[k].w;
            k=e[k].next;
        }
    }
    printf("%d\n",d[te]);
    return 0;
}

//1379 热浪(heatwv)

//P1339 [USACO09OCT]热浪Heat Wave

http://blog.csdn.net/mrcrack/article/details/70112975

//准备试试SPFA算法,这篇文章介绍得不错http://blog.csdn.net/muxidreamtohit/article/details/7894298
//邻接表方式
//样例很快通过,提交,测试点3,7,8,10WA
//查出的问题还不少,主要有两处:vis[u]=0;//2 漏了此句
//if(vis[v]==0){//3 整个if语句写到上面if语句的外面,写错位置了 提交AC
#include <stdio.h>
#include <string.h>
#define INF 9999;
struct node{
    int to,next,w;
}e[6200*2+100];
int q[2500+100],h,t,cnt=0,head[2500+100],vis[2500+10],d[2500+10];
void add(int u,int v,int w){//无向图,加入边,采用邻接表形式
    cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;
    cnt++,e[cnt].to=u,e[cnt].w=w,e[cnt].next=head[v],head[v]=cnt;
}
int main(){
    int t,c,ts,te,i,j,u,v,w,b;
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
    scanf("%d%d%d%d",&t,&c,&ts,&te);
    for(i=1;i<=c;i++){
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for(i=1;i<=t;i++)
        d[i]=INF;
    d[ts]=0;
    h=t=1;
    q[t]=ts;
    vis[ts]=1;
    t++;
    while(h<t){
        u=q[h];
        b=head[u];
        while(b!=0){
            v=e[b].to;
            if(d[v]>d[u]+e[b].w){//1 此处写成 if(d[v]<d[u]+e[b].w)低级错误
                d[v]=d[u]+e[b].w;
                if(vis[v]==0){//3 整个if语句写到上面if语句的外面,写错位置了
                    vis[v]=1;
                    q[t]=v;
                    t++;
                }
            }
            b=e[b].next;
        }
        vis[u]=0;//2 漏了此句
        h++;
    }
    printf("%d\n",d[te]);
    return 0;
}

2017-6-18 13:54 补充SPFA算法。

//1380 分糖果(candy)
//感觉与树的深度有关
//即糖果从树根下落,每一层上的小朋友都要得到糖果,糖果最多能到的层数
//大致有了基本思路,算最短路径,之后将路径排序,按由小到大的路径进行糖果分配
//该题的关键,又到了最短路径,问题是:1≤n≤100000 如何存储边的数据?
//c的数据没有给定范围,这样,数据先开到系统能承受的范围
//采用邻接表,SPFA算法
//样例通过,提交,测试点7-8答案错误,测试点9-10运行错误
//http://codevs.cn/problem/2197/里提交AC
//将 e[maxn*10] 改成 e[maxn*200] 提交AC 2017-12-23 17:31
//经测试 数组需开到 e[maxn*20];
#include <stdio.h>
#include <string.h>
#define maxn 100100
#define INF 999999999
int n,p,c,m,head[maxn],cnt=0,q[maxn*10],d[maxn],vis[maxn];
struct node{
    int to,next,w;
}e[maxn*20];//对 e[maxn*10] 数据范围心存疑虑
void add_edge(int u,int v,int w){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],e[cnt].w=w,head[u]=cnt;
}
void SPFA(int s){
    int i,u,v,b,h,t;
    for(i=1;i<=n;i++)d[i]=INF;
    d[s]=0,h=t=1,vis[s]=1,q[t]=s,t++;
    while(h<t){
        u=q[h];
        for(b=head[u];b;b=e[b].next){
            v=e[b].to;
            if(d[v]>d[u]+e[b].w){
                d[v]=d[u]+e[b].w;
                if(vis[v]==0){
                    q[t]=v,t++,vis[v]=1;
                }
            }
        }
        h++;
    }
}
int main(){
    int i,u,v,max=0,j;
    memset(head,0,sizeof(head)),memset(vis,0,sizeof(vis));
    scanf("%d%d%d%d",&n,&p,&c,&m);
    for(i=1;i<=p;i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v,1),add_edge(v,u,1);
    }
    SPFA(c);
    for(i=1;i<=n;i++)
        if(d[i]>max)max=d[i];
    printf("%d",max+m+1);
    return 0;
}

 

 

//1381 城市路(Dijkstra)
//看了数据范围,1<=n<=2000 1<=m<=10000邻接表存储图比较合适
//样例通过,提交,测试点5,7,9,10,11答案错误
//突然想到无向图,边数要*2,提交,测试点5 答案错误
//用输出结果全为-1测试了一下,发现,测试点全部错误
//急需 测试点5 数据,或他人代码,搜索网络无果,重新读题,偶然发现"当然也可能有多条"
//立马明白,在多条中,选择最短一条。
//重改代码,将邻接表改成邻接矩阵
//修改,提交AC 2017-11-27
//在想,遇到重边,用邻接表如何处理
//对邻接表如何处理,进行了深入思考,实现,没问题,但是相对邻接矩阵麻烦多了
#include <stdio.h>
#include <string.h>
#define INF 999999999
int a[2100][2100],cnt=0,n,m,d[2100],vis[2100];
int findMin(){
    int i,k,min=999999999;
    for(i=1;i<=n;i++)
        if(vis[i]==0&&d[i]<min)min=d[i],k=i;
    return k;
}
void Dijkstra(){
    int i,count=1,k;
    for(i=1;i<=n;i++)d[i]=a[1][i];
    vis[1]=1;
    while(count<n){
        count++;
        k=findMin(),vis[k]=1;
        for(i=1;i<=n;i++)
            if(vis[i]==0&&d[i]>d[k]+a[k][i])d[i]=d[k]+a[k][i];
    }
}
int main(){
    int i,j,u,v,w;
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(i==j)a[i][j]=0;
            else a[i][j]=INF;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        if(a[u][v]>w)a[u][v]=w,a[v][u]=w;//多条边处理
    }
    Dijkstra();
    if(d[n]==INF)printf("-1");
    else printf("%d",d[n]);
    return 0;

 

}

 

//1381 城市路(Dijkstra)

// 一直对 邻接表 如何处理 重边念念不忘,天天想,翻资料,偶然间想到了解决方法,初始化d[i]时要注意,其他处理手法照旧。修改代码,提交AC 2017-11-30

 

#include <stdio.h>
#include <string.h>
#define INF 999999999
int head[2100],cnt=0,n,m,d[2100],vis[2100];
struct node{
    int to,w,next;
}e[30200];//此处写成 e[10100]
void add_edge(int u,int v,int w){
    cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;
}
int findMin(){
    int i,k,min=999999999;
    for(i=1;i<=n;i++)
        if(vis[i]==0&&d[i]<min)min=d[i],k=i;
    return k;
}
void Dijkstra(){
    int i,b,u,v,count=1,k,w;//b边 
    for(i=1;i<=n;i++)d[i]=INF;
    d[1]=0,vis[1]=1;
    for(b=head[1];b;b=e[b].next){
	v=e[b].to;
	if(d[v]>e[b].w)	d[v]=e[b].w;//添加此行判断,处理重边 针对测试点5
    }
    while(count<n){
        count++;
        k=findMin(),vis[k]=1;
        for(b=head[k];b;b=e[b].next){
            v=e[b].to,w=e[b].w;
            if(vis[v]==0&&d[v]>d[k]+w)d[v]=d[k]+w;
        }
    }
}
int main(){
    int i,j,u,v,w;
    memset(head,0,sizeof(head)),memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        add_edge(u,v,w),add_edge(v,u,w);//无向图 
    }
    Dijkstra();
    if(d[n]==INF)printf("-1");
    else printf("%d",d[n]);
    return 0;
}

//1382 最短路(Spfa)

//有幸再次编写该题,没有翻任何资料,一把成功AC,编的过程中,不断有记忆涌现,一些易错的点全部涌现出来,感觉这是能力记忆的体现,化为自身的能力了。2018-9-19 21:18

#include <stdio.h>
#include <string.h>
#define maxm 500100
#define maxn 100100
#define INF 900000000
int head[maxn],cnt=0,n,m,q[10*maxn],d[maxn],vis[maxn];
struct node{
    int to;
    int w;
    int next;
}e[maxm*2];
void addEdge(int u,int v,int w){//无向图 
    cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;
}
void spfa(){
    int h,t,u,v,w,b,i;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;i++)d[i]=INF;
    d[1]=0;
    h=t=0;
    q[t]=1,vis[1]=1,t++;
    while(h<t){
        u=q[h];
        b=head[u];
        while(b){
            v=e[b].to;
            w=e[b].w;
            if(vis[v]==0&&d[v]>d[u]+w){
                d[v]=d[u]+w;
                q[t]=v;
                t++;
            }
            b=e[b].next;
        }
        vis[u]=0;
        h++;
    }
    printf("%d\n",d[n]);
}
int main(){
    int i,j,u,v,w;
    memset(head,0,sizeof(head));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        addEdge(u,v,w),addEdge(v,u,w);
    }
    spfa();
    return 0;
}

//1382 最短路(Spfa)
//样例通过,提交,测试点3 运行超时
//参考了《信息学奥赛一本通(C++版)》将 q[100000+100] 改成  q[100000*10+100]
//仔细一想确实如此,因一个点可能多次放入队列中,故队列开到点的10倍为好,提交AC 2017-12-22
//至此,该题已被搁置10天之久。
#include <stdio.h>
#include <string.h>
#define INF 999999999;
struct node{
    int to,next,w;
}e[500000*2+100];
int q[100000*10+100],h,t,cnt=0,head[100000+100],vis[100000+10],d[100000+10];//q[100000+100]测试点3 运行超时
void add(int u,int v,int w){//无向图,加入边,采用邻接表形式
    cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;
    cnt++,e[cnt].to=u,e[cnt].w=w,e[cnt].next=head[v],head[v]=cnt;
}
int main(){
    int n,m,i,j,u,v,w,b;
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for(i=1;i<=n;i++)
        d[i]=INF;
    d[1]=0;
    h=t=1;
    q[t]=1;
    vis[1]=1;
    t++;
    while(h<t){
        u=q[h];
        b=head[u];
        while(b!=0){
            v=e[b].to;
            if(d[v]>d[u]+e[b].w){//1 此处写成 if(d[v]<d[u]+e[b].w)低级错误
                d[v]=d[u]+e[b].w;
                if(vis[v]==0){//3 整个if语句写到上面if语句的外面,写错位置了
                    vis[v]=1;
                    q[t]=v;
                    t++;
                }
            }
            b=e[b].next;
        }
        vis[u]=0;//2 漏了此句
        h++;
    }
    printf("%d\n",d[n]);
    return 0;
}

2017-12-24 21:46 AC 该节内容
第三节 图的连通性问题
//1383 刻录光盘(cdrom)
//样例通过,提交,测试点7,8,9答案错误
//想添加入度进行描述,发现题中样例都通不过。
//网上搜索了一通,http://blog.csdn.net/cherish0222/article/details/57079677也只有该代码能通过
//受到启发,该题用并查集处理.
//代码重写,经编写,发现不成功,同样,测试点7,8,9答案错误
//一通研究,发现代码像并查集,但又不是并查集。
//样例通过,提交AC 2017-12-30 21:33
//觉得代码写得不够漂亮,还是决定再重写代码。
//样例通过,提交AC 2017-12-30 21:40
//提供一组输入输出数据
//输入:
//12
//2 6 0
//3 0
//4 0
//5 0
//1 0
//7 0
//8 0
//9 0
//10 0
//1 0
//1 0
//1 0
//1 0
//输出:
//3
#include <stdio.h>
#include <string.h>
int a[210][210],pre[210];//pre[i] i节点对应的根节点
int main(){
    int n,i,j,k,ans=0;
    memset(a,0,sizeof(a));
    scanf("%d",&n);
    for(i=1;i<=n;i++)pre[i]=i;
    for(i=1;i<=n;i++)
        while(scanf("%d",&j)&&j)a[i][j]=1;
    for(i=1;i<=n;i++)
        for(k=1;k<=n;k++)
            for(j=1;j<=n;j++)
                if(a[i][k]&&a[k][j])a[i][j]=1;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(a[i][j])pre[j]=pre[i];
    for(i=1;i<=n;i++)
        if(pre[i]==i)ans++;
    printf("%d",ans);
    return 0;
}

//1384 珍珠(bead)
//http://www.oier.cc/ssoj2427%E7%8F%8D%E7%8F%A0/此文代码写得真不赖
//样例通过,提交AC 2017-12-30 22:18
#include <stdio.h>
#include <string.h>
int a[110][110];
int main(){
    int i,j,k,u,v,n,m,ans=0;
    memset(a,0,sizeof(a));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&u,&v),a[u][v]=1;
    for(k=1;k<=n;k++)//类似Floyd算法
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(a[i][k]&&a[k][j])//i重于k k重于j 推出 i重于j
                    a[i][j]=1;//i重于j
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            a[0][j]+=a[i][j],a[i][0]+=a[i][j];//a[0][j]统计重于j的珍珠个数 a[i][0]统计轻于i的珍珠个数
    for(i=1;i<=n;i++)
        if(a[0][i]>n/2||a[i][0]>n/2)
            ans++;
    printf("%d",ans);
    return 0;
}

2017-12-30 22:18  AC 该节内容
第四节 并查集
//1346 【例4-7】亲戚(relation)

 

//2017-11-11 23:05 AC

#include <stdio.h>
int f[20100];
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
void merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2)
        f[f2]=f1;
}
int main(){
    int i,n,m,u,v,q;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        merge(u,v);
    }
    for(i=1;i<=n;i++)f[i]=getf(i);//添加此句提高效率
    scanf("%d",&q);
    for(i=1;i<=q;i++){
        scanf("%d%d",&u,&v);
        if(f[u]==f[v])printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
//1347 【例4-8】格子游戏
//此文介绍得不错,http://blog.csdn.net/clove_unique/article/details/48682185摘抄如下:
//这道题让我学会了用结构体实现二维的并查集。其实也并不是很难,在每一次比较的时候只需要把x和y都比较一下就可以了;
//每一次划线,先判断一下,如果起点在集合里,终点也在集合里,那么符合条件,输出当时的步数就可以了;如果不符合上面的条件,把点入集合;如果到最后还没有符合条件的情况,输出draw就可以了。
//样例通过,修改,提交AC 2017-11-18 9:53
#include <stdio.h>
struct node{
    int r,c;
}f[210][210],f1,f2;
struct node getf(struct node f3){
    if(f[f3.r][f3.c].r==f3.r&&f[f3.r][f3.c].c==f3.c)return f3;
    return f[f3.r][f3.c]=getf(f[f3.r][f3.c]);
}
int main(){
    int n,m,i,j,r,c;
    char cmd[5];
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            f[i][j].r=i,f[i][j].c=j;
    for(i=1;i<=m;i++){
        scanf("%d%d%s",&r,&c,cmd);
        if(cmd[0]=='D'){
            f1=getf(f[r][c]),f2=getf(f[r+1][c]);
        }else if(cmd[0]=='R'){
            f1=getf(f[r][c]),f2=getf(f[r][c+1]);
        }
        if(f1.r==f2.r&&f1.c==f2.c){
            printf("%d",i);
            return 0;
        }else{//合并 左靠
            f[f2.r][f2.c]=f1;
        }
    }
    printf("draw");
    return 0;
}
//1385 团伙(group)
//https://www.cnblogs.com/GXZlegend/p/6405309.html受启发,开始编码
//将n个人扩充到2*n个人,1-n与n+1-2n人对立
//该题,在1-n中找不同的爸爸个数
//AC 2017-11-12 22:39
#include <stdio.h>
#include <string.h>
int f[2100],vis[2100];
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
void merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2)f[f2]=f1;
}
int main(){
    int n,m,i,p,x,y,cnt=0,father;
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=1;i<=2*n;i++)f[i]=i;
    while(m--){
        scanf("%d%d%d",&p,&x,&y);
        if(p==0)merge(x,y);
        else merge(x,y+n),merge(x+n,y);
    }
    for(i=1;i<=n;i++){
        father=getf(i);
        if(vis[father]==0)
            vis[father]=1,cnt++;
    }
    printf("%d",cnt);
    return 0;
}

//1386 打击犯罪(black)
//"第一个整数表示该行除第一个外还有多少个整数"这句话花了些时间,对照输入样例总算弄明白了
//看了样例的图后,感觉这题在求关节点,似乎与并查集扯不上关系
//http://blog.csdn.net/u011123263/article/details/39010779此文介绍得不错,代码也够短,思路摘抄如下:
//题解:我们求删点完后剩下的图组成的集合,假设删点删到了k那剩下的图就是由k~n之间的    
//点组成,如果他们组成的结合最大子图节点数不大(n+1)/2则说明k不该删,则继续往前推.(注意:删除1~k)  
//http://www.cnblogs.com/fujudge/p/7577741.html此文用邻接表写的,感觉很对胃,思路摘抄如下:
//不容易删点,所以考虑倒着加点,找到一组不满足要求了输出即可。
//样例通过,提交AC 2017-12-1 20:57
#include <stdio.h>
#include <string.h>
#define maxn 1010
struct node{
    int to,next;
}e[maxn*maxn];
int head[maxn],cnt=0,f[maxn],size[maxn];
void add_edge(int u,int v){//邻接表方式
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int getf(int u){//找爸爸
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
int main(){
    int n,i,j,t,k,f1,f2,b;//b边
    memset(head,0,sizeof(head));
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        f[i]=i,size[i]=1;
        scanf("%d",&t);
        for(j=1;j<=t;j++){
            scanf("%d",&k);
            add_edge(i,k),add_edge(k,i);//无向图
        }
    }
    for(i=n;i>=1;i--){//逆向添加
        f1=getf(i);
        for(b=head[i];b;b=e[b].next){
            j=e[b].to;
            if(j>i){//在i之前的点
                f2=getf(j);
                if(f1!=f2){
                    f[f2]=f1;
                    size[f1]+=size[f2];
                    if(size[f1]>n/2){
                        printf("%d",i);
                        return 0;
                    }
                }
            }
        }
    }
    return 0;
}

 

//1387 搭配购买(buy)
//粗看题目,觉得挺难的,但细细一读,发现,属同一个爸爸的都需要购买
//在买得起的集团中选价值最大的。
//提交,只有测试点1,8答案正确
//看了这篇文章http://blog.csdn.net/xiaofang3a/article/details/39324067的文字,才明白,想法确实有误,摘抄如下
//买ui就必须买vi,同理,如果买vi就必须买ui。可以用并查集把这这物品些归结到一起,形成一个新的物品,然后重新得到一系列物品,然后使用01背包就OK了。
//此文http://blog.csdn.net/u011123263/article/details/39312835代码与本人相似较高,研究01背包部分
//修改,提交AC。2017-11-20 22:17
//本题证明,并查集掌握炉火纯青,但01背包还有欠缺,尤其是一维数组形式
#include <stdio.h>
#include <string.h>
int c[10100],d[10100],f[10100],dp[10100];
int max(int a,int b){
    return a>b?a:b;
}
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
void merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2)f[f2]=f1,c[f1]+=c[f2],d[f1]+=d[f2];
}
int main(){
    int n,m,w,i,j,u,v;
    memset(dp,0,sizeof(dp));//别忘了初始化
    scanf("%d%d%d",&n,&m,&w);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<=n;i++)scanf("%d%d",&c[i],&d[i]);
    for(i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        merge(u,v);
    }
    for(i=1;i<=n;i++)getf(i);//更新最新的爸爸
    for(i=1;i<=n;i++)
        if(f[i]==i){
            for(j=w;j>=c[i];j--)
                dp[j]=max(dp[j],dp[j-c[i]]+d[i]);//01背包的一维数组优化要好好学学
        }
    printf("%d",dp[w]);
}

 

//1388 家谱(gen)
//该题的难点在于,数据是字符串,如何将字符串映射成数字是难点
//翻看了一下,基本上都要用到map,那好吧,开始学习。
//http://blog.csdn.net/xiaofang3a/article/details/39583367此文代码写得够好,map用得够漂亮,值得学习
//用C++来编写
//样例通过,提交,测试点6,7,8答案错误
//洛谷 P2814 家谱 提交,同样 测试点6,7,8 WA
//在正确代码的基础上,进行代码修改,逐渐逼近本人代码,发现了问题所在,找到的爸爸可能还不是祖宗。
//修改,提交AC 2017-11-20
#include <iostream>
#include <string>
#include <map>
using namespace std;
map<string,string> f;
string getf(string a){
    if(f[a]==a)return a;
    return f[a]=getf(f[a]);
}
int main(){
    string s,b,c,d;
    while(cin>>s&&s!="$"){
        if(s[0]=='#'){
            b=s.substr(1,s.size()-1);
            if(f[b]=="")f[b]=b;
        }else if(s[0]=='+'){
            c=s.substr(1,s.size()-1);
            getf(b);
            f[c]=f[b];
        }else if(s[0]=='?'){
            d=s.substr(1,s.size()-1);
            getf(d);//漏了此句, 测试点6,7,8 WA
            cout<<d<<" "<<f[d]<<endl;
        }
    }
    return 0;
}

 

//1389 亲戚
//样例通过,提交AC,请注意,此刻,网络上还搜索不到该题的解法。2017-11-20
#include <stdio.h>
int f[100100],cnt[100100];//cnt[i]统计以i为父亲的家族人数
int getf(int a){
    if(f[a]==a)return a;
    return f[a]=getf(f[a]);
}
void merge(int a,int b){//左靠
    int f1=getf(a),f2=getf(b);
    if(f1!=f2)
        f[f2]=f1,cnt[f1]+=cnt[f2];//此句很关键 cnt[f1]+=cnt[f2],自个想到的,真是很佩服自己
}
int main(){
    int n,m,i,j,a,b;
    char cmd[10];
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)f[i]=i,cnt[i]=1;
    while(m--){
        scanf("%s",cmd);
        switch(cmd[0]){
            case 'M':
                scanf("%d%d",&a,&b);
                merge(a,b);
                break;
            case 'Q':
                scanf("%d",&a);
                printf("%d\n",cnt[getf(a)]);
                break;
        }
    }
    return 0;
}

 

 

//1390 食物链

2.//P2024 食物链

http://blog.csdn.net/mrcrack/article/details/70112975
//靠自己,想不到要分成三块x的同类,x吃的动物,被x吃的动物
//f[x] f[x+n] f[x+2*n]x的同类,x吃的动物,吃x的动物
//f[y] f[y+n] f[y+2*n]y的同类,y吃的动物,吃y的动物 
//审题不清,没往脑子里去:A 吃 B,B吃 C,C 吃 A。 
//http://www.cnblogs.com/zbtrs/p/5819576.html分析得不错,摘抄如下 : 
//分析:吃与被吃和同类是三种逻辑关系,而三种逻辑关系对应的动物又有很多,而动物之间又可以形成集合,所以考虑并查集.因为有3种逻辑关系,所以用大小n*3的并查集.
//可以参考noip2010的关押罪犯,对于一个犯人而言,另一个犯人只有可能和他在同一个监狱或者在不同监狱,那么建立n*2大小的并查集,那么对于本题也是一样,用x表示和x同种的动物集合,x+n表示被x吃的动物,x+2*n表示吃x的动物,那么对于另一种动物y,如果是第一种说法,那么x和y一定是同类(废话......),那么如果y在x+n的集合中或者y在x+2*n的集合中,就是假话,当然,要先判断x或y是否大于n并且是否相等,之后合并一下x和y,x+n和y+n,x+2*n和y+2*n.
//那么对于第二种说法呢?如果x和y是同类,那么肯定是假话,如果y吃x,那么肯定也是假话,那么判断x和y或者x+2*n和y是否在同一个集合即可,之后合并x和y + 2 * n,x + n和 y,x + 2*n和y + n(因为食物链是环,吃x的必然被y吃),那么本题就结束了.
//提交30分,只AC了测试点1,3,9明白是判断同类,异类出了问题。 修改,提交还是30分
//错在:如果是第一种说法,那么x和y一定是同类(废话......),那么如果y在x+n的集合中或者y在x+2*n的集合中,就是假话
//错在:那么对于第二种说法呢?如果x和y是同类,那么肯定是假话,如果y吃x,那么肯定也是假话
//归根结底,对题目分析不够,望日后有重做的机会2017-6-4 23:18 
#include <stdio.h>
int f[3*50000+100];
int getf(int z){
    if(f[z]==z)
        return z;
    f[z]=getf(f[z]);
    return f[z];
}
void merge(int a, int b){//靠左规则 
    int f1=getf(a),f2=getf(b);
    if(f1!=f2)
        f[f2]=f1;
}
int main(){
    int n,k,i,j,x,y,t,ans=0;
    scanf("%d%d",&n,&k);
    for(i=1;i<=3*n;i++)
        f[i]=i;
    for(i=1;i<=k;i++){
        scanf("%d%d%d",&t,&x,&y);
        if(x>n||y>n)
            ans++;
        else if(t==1){
            if(getf(x+n)==getf(y)||getf(x+2*n)==getf(y))//2getf(x+n)==getf(y)||getf(x)==getf(y+n) x吃y 或者 y吃x 非同类 1getf(x+n)==getf(y)||getf(x+2*n)==getf(y+n)||getf(x)==getf(y+2*n)
                ans++;
            else{
                merge(x,y);
                merge(x+n,y+n);
                merge(x+2*n,y+2*n);
            } 
        }else if(t==2){
            if(getf(x)==getf(y)||getf(x+2*n)==getf(y))//2getf(x)==getf(y)同类 同类1getf(x)==getf(y)||getf(x+n)==getf(y+n)||getf(x+2*n)==getf(y+2*n)
                ans++;
            else{
                merge(x+n,y);
                merge(x+2*n,y+n);
                merge(x,y+2*n);
            } 
        }
    }
    printf("%d\n",ans);
    return 0;
}

2017-12-1 20:57 AC 该节内容
第五节 最小生成树

//1348 【例4-9】城市公交网建设问题
//使用Prim算法未果,多次修改均无效,网上搜索了一堆代码,发现,全是无法AC的代码。
//采用Kruskal算法
//很快样例通过,提交AC 2017-12-4
//该题有几点要注意:
//输出,按边的第一个点的序号自小到大输出,若第一个序号相同,按第二个点的序号自小到大输出
//要打印加入路径,Kruskal算法绝对要好过Prim算法,Prim算法与Dijkstra算法比较接近。2017-12-4
#include <stdio.h>
int f[110];
struct node{
    int a,b,w;
}e[110*110],e_t,d[110];
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
int merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2){
        f[f2]=f1;
        return 1;
    }
    return 0;
}
void quicksort(int left,int right){//自小到大排列
    int i=left,j=right,mid=e[(left+right)/2].w;
    while(i<=j){
        while(e[i].w<mid)i++;
        while(e[j].w>mid)j--;
        if(i<=j)e_t=e[i],e[i]=e[j],e[j]=e_t,i++,j--;
    }
    if(i<right)quicksort(i,right);
    if(left<j)quicksort(left,j);
}
int main(){
    int n,m,i,j,u,v,w,t,cnt=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        if(u>v)t=u,u=v,v=t;//让小的点在前面
        e[i].a=u,e[i].b=v,e[i].w=w;
    }
    quicksort(1,m);
    for(i=1;i<=m;i++)
        if(merge(e[i].a,e[i].b)){
            cnt++;
            d[cnt].a=e[i].a,d[cnt].b=e[i].b,d[cnt].w=e[i].w;
            if(cnt==n-1)break;
        }
    for(i=1;i<=cnt;i++)//自小到大排序
        for(j=i+1;j<=cnt;j++)
            if(d[i].a>d[j].a)e_t=d[i],d[i]=d[j],d[j]=e_t;
            else if(d[i].a==d[j].a&&d[i].b>d[j].b)e_t=d[i],d[i]=d[j],d[j]=e_t;
    for(i=1;i<=cnt;i++)
        printf("%d %d\n",d[i].a,d[i].b);
    return 0;
}

 

//1349 【例4-10】最优布线问题
//最小生成树 采用Prim算法
//样例通过,提交,只有测试点1答案正确,静态检查代码,发现 scanf("%d",&n);//此处写成 scanf("%d%d",&n);只对了测试点1
//修改,提交AC 2017-12-3 21:28
#include <stdio.h>
#include <string.h>
#define INF 999999999
int vis[110],d[110],a[110][110],n;//d[]到生成树的最短距离
int findMin(){//返回未访问过的最小的d[i]的i值
    int i,j,min=999999999;
    for(i=1;i<=n;i++)
        if(vis[i]==0&&d[i]<min)min=d[i],j=i;
    return j;
}
void Prim(){
    int i,j,k;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;i++)d[i]=a[1][i];
    for(i=1;i<=n;i++){//n个点
        k=findMin(),vis[k]=1;
        for(j=1;j<=n;j++)
            if(vis[j]==0&&d[j]>a[k][j])d[j]=a[k][j];
    }
}
int main(){
    int i,j,u,v,w,sum=0;
    scanf("%d",&n);//此处写成 scanf("%d%d",&n);只对了测试点1
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf("%d",&a[i][j]);
    Prim();
    for(i=1;i<=n;i++)sum+=d[i];
    printf("%d",sum);
    return 0;
}
//1350:【例4-11】最短网络(agrinet)
//Kruskal
//没有参考任何资料,独立编写,往日的一些注意事项涌现出来,很成功,一把AC。
//仔细思考了 并查集 在此种算法中的作用。
//代码编写还是花了些时间,主要花在思考上。2018-9-19 22:20 
#include <stdio.h>
int n,b=0,f[110];
struct node{
    int u,v,w;
}e[100*100/2],t;
void quickSort(int left,int right){//自小到大排序 
    int i=left,j=right,mid=e[(left+right)/2].w;
    while(i<=j){
        while(e[i].w<mid)i++;
        while(e[j].w>mid)j--;
        if(i<=j){
            t=e[i],e[i]=e[j],e[j]=t,i++,j--;
        }
    }
    if(i<right)quickSort(i,right);
    if(left<j)quickSort(left,j);
}
int find(int u){
    if(f[u]==u)return u;
    return f[u]=find(f[u]);
}
int merge(int u,int v){//左靠 
    int f1=find(u),f2=find(v);
    if(f1==f2)return 0;
    f[f2]=f1;
    return 1;
}
int main(){
    int i,j,w,cnt=0,ans=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            scanf("%d",&w);
            if(j>i)b++,e[b].u=i,e[b].v=j,e[b].w=w;
        }
    quickSort(1,b);
    for(i=1;i<=n;i++)f[i]=i;
    while(cnt<n-1){
        cnt++;
        
    }
    for(i=1;i<=b;i++){
        if(merge(e[i].u,e[i].v))
            cnt++,ans+=e[i].w;
        if(cnt==n-1)break;
    }
    printf("%d\n",ans);
    return 0;
}

//1350 【例4-11】最短网络(agrinet) 2017-12-3 21:33 AC
//代码与 1349 【例4-10】最优布线问题 完全一致
//1349 【例4-10】最优布线问题
//最小生成树 采用Prim算法
//样例通过,提交,只有测试点1答案正确,静态检查代码,发现 scanf("%d",&n);//此处写成 scanf("%d%d",&n);只对了测试点1
//修改,提交AC 2017-12-3 21:28
#include <stdio.h>
#include <string.h>
#define INF 999999999
int vis[110],d[110],a[110][110],n;//d[]到生成树的最短距离
int findMin(){//返回未访问过的最小的d[i]的i值
    int i,j,min=999999999;
    for(i=1;i<=n;i++)
        if(vis[i]==0&&d[i]<min)min=d[i],j=i;
    return j;
}
void Prim(){
    int i,j,k;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;i++)d[i]=a[1][i];
    for(i=1;i<=n;i++){//n个点
        k=findMin(),vis[k]=1;
        for(j=1;j<=n;j++)
            if(vis[j]==0&&d[j]>a[k][j])d[j]=a[k][j];
    }
}
int main(){
    int i,j,u,v,w,sum=0;
    scanf("%d",&n);//此处写成 scanf("%d%d",&n);只对了测试点1
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf("%d",&a[i][j]);
    Prim();
    for(i=1;i<=n;i++)sum+=d[i];
    printf("%d",sum);
    return 0;
}

//1351 【例4-12】家谱树
//画图模拟了该题,发现可以用拓扑排序来处理
//样例通过,提交AC 2017-12-14 21:28
#include <stdio.h>
#include <string.h>
int head[110],cnt=0,rd[110],n,vis[110],q[110];
struct node{
    int to,next;
}e[110*110/2];
void add_edge(int u,int v){//有向图
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void topsort(int k){
    int h,t,u,v,b;
    h=t=1,q[t]=k,t++,vis[k]=1;
    while(h<t){
        u=q[h],printf("%d ",u);
        for(b=head[u];b;b=e[b].next){
            v=e[b].to;
            rd[v]--;
            if(rd[v]==0)q[t]=v,t++,vis[v]=1;
        }
        h++;
    }
}
int main(){
    int i,j;
    memset(rd,0,sizeof(rd)),memset(vis,0,sizeof(vis));
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        while(scanf("%d",&j)&&j)
            add_edge(i,j),rd[j]++;
    for(i=1;i<=n;i++)
        if(vis[i]==0&&rd[i]==0)
            topsort(i);
    return 0;
}

 

//1391 局域网(net)
//求最小生成树,之后,剩下的边之和即为答案
//该题第一直觉就是采用Kruskal算法
//样例通过,提交AC 2017-12-4 21:20
#include <stdio.h>
int n,k,f[110];
struct node{
    int a,b,w;
}e[110*110],e_t;
void quicksort(int left,int right){
    int i=left,j=right,mid=e[(left+right)/2].w;
    while(i<=j){
        while(e[i].w<mid)i++;
        while(e[j].w>mid)j--;
        if(i<=j)e_t=e[i],e[i]=e[j],e[j]=e_t,i++,j--;
    }
    if(i<right)quicksort(i,right);
    if(left<j)quicksort(left,j);
}
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
int merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2){
        f[f2]=f1;
        return 1;
    }
    return 0;
}
int Kruskal(){
    int i,cnt=0,ans=0;
    for(i=1;i<=k;i++)
        if(merge(e[i].a,e[i].b)){
            ans+=e[i].w;
            cnt++;
            if(cnt==n-1)break;
        }
    return ans;
}
int main(){
    int i,j,a,b,w,sum=0;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<=k;i++){
        scanf("%d%d%d",&a,&b,&w);
        e[i].a=a,e[i].b=b,e[i].w=w;
    }
    quicksort(1,k);
    for(i=1;i<=k;i++)sum+=e[i].w;
    printf("%d",sum-Kruskal());
    return 0;
}


//1392 繁忙的都市(city)
//想用Kruskal算法,但是为了练习Prim算法,还是采用后者 
//编写的过程中,遇到不少问题,仔细想了想,还是受到了输入数据的影响
//以前的输入是邻接矩阵的形式,现在是以边的形式给出。 
//样例通过,提交AC 2017-12-4 20:51 
#include <stdio.h>
#include <string.h>
#define INF 999999999
int a[310][310],d[310],n,m,vis[310];
int findMin(){
    int i,min=INF,k;
    for(i=1;i<=n;i++)
        if(vis[i]==0&&d[i]<min)min=d[i],k=i;
    return k;
}
void Prim(){
    int i,j,k;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;i++)d[i]=a[1][i];//此处写成 d[i]=INF
    for(i=1;i<=n;i++){
        k=findMin(),vis[k]=1;
        for(j=1;j<=n;j++)
            if(vis[j]==0&&d[j]>a[k][j])d[j]=a[k][j];
    }
}
int main(){
    int i,j,u,v,w,max=-1;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)//忘记了初始化 
        for(j=1;j<=n;j++)
            if(i==j)a[i][j]=0;
            else a[i][j]=INF;
            
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        a[u][v]=a[v][u]=w;
    }
    Prim();
    for(i=1;i<=n;i++)
        if(d[i]>max)max=d[i];
    printf("%d %d\n",n-1,max);
    return 0;

//1393 联络员(liaison)
//采用Kruskal算法,对数据进行排序,p=1必选排前,p=2可选按费用自小到大排序
//样例通过,提交AC 2017-12-15
//该题核心思想还是Kruskal算法,只是根据环境进行算法的略微变化,有一定收获,至少调用系统的排序算法,用得还不错.
#include <cstdio>
#include <algorithm>
using namespace std;
int n,m,f[2100];
struct node{
    int p,u,v,w;
}e[10100];
int cmp(struct node a,struct node b){
    if(a.p==2&&b.p==2)return a.w<=b.w;
    return a.p<=b.p;
}
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
int merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2){
        f[f2]=f1;
        return 1;
    }
    return 0;
}
int main(){
    int i,cnt=0,ans=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<=m;i++)scanf("%d%d%d%d",&e[i].p,&e[i].u,&e[i].v,&e[i].w);
    sort(e+1,e+1+m,cmp);
    for(i=1;i<=m;i++)//p==1均需选用,故存在环 ,故p==2所有的边均需判定
        if(e[i].p==1)
            ans+=e[i].w,merge(e[i].u,e[i].v);
        else if(e[i].p==2)
            if(merge(e[i].u,e[i].v))
                ans+=e[i].w;
    printf("%d",ans);
    return 0;
}
 
//1394 连接格点(grid)
//难点1,并查集只适用于一维数据,要将题中二维数据转成一维数据,这点确实没想到
//难点2,要处理相邻节点,想过,但没动手,眼高手低
//http://blog.csdn.net/keyword_/article/details/77800006此文代码写得精炼,值得学习
//样例通过,提交AC 2017-12-16 21:43
#include <stdio.h>
int f[1000100];
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
int merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2){//左靠
        f[f2]=f1;
        return 1;
    }
    return 0;
}
int main(){
    int m,n,i,j,x1,y1,x2,y2,ans=0;
    scanf("%d%d",&m,&n);
    for(i=1;i<=m*n;i++)f[i]=i;
    while(scanf("%d%d%d%d",&x1,&y1,&x2,&y2)==4)merge((x1-1)*n+y1,(x2-1)*n+y2);
    for(j=1;j<=n;j++)//先连竖线,竖线只需一个单位
        for(i=1;i<m;i++)
            if(merge((i-1)*n+j,(i)*n+j))ans+=1;
    for(i=1;i<=m;i++)//后连横线,横线需两个单位
        for(j=1;j<n;j++)
            if(merge((i-1)*n+j,(i-1)*n+j+1))ans+=2;
    printf("%d",ans);
}

2017-12-16 21:43 AC 该节内容

第六节 拓扑排序与关键路径
//1352 【例4-13】奖金
//第一判断是否有环,若有,则无法找到合理方案
//关系输入为1 2存储为2 1,对各点计算入度,感觉要用到深度优先遍历
//写起来,要考虑的情况有些复杂,如某个节点有两个分支,处理起来比较棘手
//https://wenku.baidu.com/view/f08bd1633b3567ec112d8a05.html该文思路与本人上述思想基本一致,
//判断环感觉遇到了问题,思路复杂度过高,跟踪每个点,看看能否回到点本身,算法复杂度n*m
//http://blog.csdn.net/u010040029/article/details/52861473受该文启发,
//算法1
//我们知道对于环1-2-3-4-1,每个节点的度都是2,基于此我们有如下算法(这是类似于有向图的拓扑排序):
//求出图中所有顶点的度,
//删除图中所有度<=1的顶点以及与该顶点相关的边,把与这些边相关的顶点的度减一
//如果还有度<=1的顶点重复步骤2
//最后如果还存在未被删除的顶点,则表示有环;否则没有环
//时间复杂度为O(E+V),其中E、V分别为图中边和顶点的数目,这个算法我们稍后分析算法3的时候再分析。
//采用广度优先遍历算法,进行拓扑排序,若,还有点未访问到,表示有环。
//拓扑排序,广度优先遍历写法,可以参考该文http://blog.csdn.net/mrcrack/article/details/78159453写得很棒
//样例通过,提交未通过,只过了测试点2
//测试了只打印Poor Xed,发现只有测试点2正确
//在http://codevs.cn/problem/6126/中提交,测试,提供下面一组测试样例
//输入:
//3 2
//1 2
//1 3
//输出:
//301
//发现问题, //将 memset(vis,0,sizeof(vis))写到topsort()中 应该写到main函数中
//在http://codevs.cn/problem/6126/中提交AC
//在http://ybt.ssoier.cn:8088/problem_show.php?pid=1352中提交AC 2017-12-6 21:12
#include <stdio.h>
#include <string.h>
int n,m,head[10100],cnt=0,vis[10100],rd[10100],f[10100],q[10100];
struct node{
    int to,next;
}e[20100];
void add_edge(int u,int v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int max(int a,int b){
    return a>b?a:b;
}
void topsort(int x){
    int h,t,u,b,v;
    h=t=1,vis[x]=1,q[t]=x,t++;
    while(h<t){
        u=q[h];
        for(b=head[u];b;b=e[b].next){
            v=e[b].to;
            rd[v]--,f[v]=max(f[v],f[u]+1);
            if(rd[v]==0){
                q[t]=v,t++,vis[v]=1;
            }
        }
        h++;
    }
}
int main(){
    int i,a,b,sum=0;
    memset(head,0,sizeof(head)),memset(rd,0,sizeof(rd)),memset(vis,0,sizeof(vis));//将 memset(vis,0,sizeof(vis))写到topsort()中//将 memset(rd,0,sizeof(rd))写到topsort()中
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)f[i]=100;
    for(i=1;i<=m;i++){
        scanf("%d%d",&b,&a);
        add_edge(a,b);
        rd[b]++;
    }
    for(i=1;i<=n;i++)
        if(vis[i]==0&&rd[i]==0)topsort(i);
    for(i=1;i<=n;i++)
        if(vis[i]==0){
            printf("Poor Xed");
            return 0;
        }
    for(i=1;i<=n;i++)sum+=f[i];
    printf("%d",sum);
    return 0;
}
//1395 烦人的幻灯片(slides)
//该题读过多遍,始终不解题意
//输入,输出 有何对应关系 始终未搞懂
//无奈,只能通过他人代码来读懂该题
//http://blog.csdn.net/MrH929/article/details/51934059此文代码较短,拿来研究
//读完代码后才弄明白
//样例中的“接下来的n行每行包括4个整数xmin,xmax,ymin,ymax”代表的是字母序列 标记 的幻灯片
//样例中的“再接下来的n行依次为n个数字编号的坐标x,y” 代表的是数字序列 标记 的幻灯片
//读题 这样的信息 怎么读都读不出。
//思路摘抄如下:
//这道题并不完全是拓扑排序题,但却用到了拓扑排序的思想
//读懂题意是关键,本题意思是每个数字都对应了一个或多个幻灯片,然后要求从中找出数字与幻灯片的一一对应关系
//节点个数很少,直接用邻接矩阵存储
//由于是数字对应幻灯片,所以我选择存储每个数字的出度,当某个数字出度为1时,寻找它所对应的幻灯片,记录,并且将与该幻灯片连接的其余数字全部断绝联系,出度也减1
//这样,n次之后就可以得到完整的一一对应关系
//本人采用拓扑排序思路,争取尽可能接近拓扑排序,进行编码
//样例,手动模拟成功

//样例通过,提交AC 2017-12-31 21:00
#include <stdio.h>
#include <string.h>
int a[30][30],rd[30],x1[30],x2[30],y1[30],y2[30],ans[30];
int main(){
    int i,j,n,x,y,k,t,m=0;
    memset(rd,0,sizeof(rd)),memset(a,0,sizeof(a));
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d%d%d%d",&x1[i],&x2[i],&y1[i],&y2[i]);//字母序列
    for(i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        for(j=1;j<=n;j++)
            if(x1[j]<=x&&x<=x2[j]&&y1[j]<=y&&y<=y2[j])
                a[i][j]=1,rd[j]++;//数字到字母的有向图,计算字母的入度
    }
    for(k=1;k<=n;k++)//循环次数为n次
        for(j=1;j<=n;j++)
            if(rd[j]==1){
                m++,rd[j]--;
                for(i=1;i<=n;i++)
                    if(a[i][j]){
                        a[i][j]=0,t=i,ans[j]=i;//查找与字母j相连的数字i
                        break;
                    }
                for(i=1;i<=n;i++)
                    if(a[t][i])
                        a[t][i]=0,rd[i]--;//删除t对应的所有边
            }
    if(n==m)
        for(i=1;i<=n;i++)printf("%c %d\n",i-1+'A',ans[i]);
    else printf("None");
    return 0;
}

//1396 病毒(virus)
//该题样例难以弄懂
//https://www.cnblogs.com/mjtcn/p/6719679.html借助此文研究该题
//换了三个小时,又是跟踪,又是模拟,终于看懂该题代码,同时也弄明白该题意思
//本题说明还是存在漏洞,题中并没有说明第一个入度为0的点对应的就是a,可能是b或c或d等等
//样例处理过程如下:

//明白题意后,该题难度就没有那么大了,该题难在破解题意,弄懂样例,没有他人AC代码作为参考,很难弄明白。
//样例通过,提交 运行错误
//if(s[a][b]==0||s[a+1][b]==0)return;//笔误,此处写成if(s[a][b]==0||s[a+b][b]==0)return; 修改,提交,还是老错误
//scanf("%s",s_t+1),len=strlen(s_t+1);//此处写成 scanf("%s",s_t+1),len=strlen(s_t); 修改,提交AC  2018-1-2 20:47
//第一次发现,运行错误出现在字符串操作上。
#include <stdio.h>
#include <string.h>
#define maxn 50100
int n,s[maxn][100],map[30][30],rd[30],m=0,word[30],ans[30];//s[maxn][100]字符串与单词映射//map[30][30]; 26个字母 word[]用来统计有效字母个数
void build(int a,int b){//建图
    if(s[a][b]==0||s[a+1][b]==0)return;//笔误,此处写成if(s[a][b]==0||s[a+b][b]==0)return;//有一个字符串结束
    if(s[a][b]!=s[a+1][b]){//建立有向图 ,计算入度
        map[s[a][b]][s[a+1][b]]=1;//因 单词 按字典序排序,故 顺序决定于 第一个 不同的字母 故该if语句 只执行一次
        rd[s[a+1][b]]++;
    }else build(a,b+1);//请注意,此处将else 删除 会出现 错误
}
int topsort(){
    int rd_num,i,p=0,k;//rd_num入度为0的个数 ,p正常字母个数
    while(p<m){
        rd_num=0;//忘了此处的初始化,将该初始化 写到while循环外了
        for(i=1;i<=m;i++)
            if(rd[i]==0)rd_num++,rd[i]=-1,k=i;//以-1形式删除该点i
        if(rd_num!=1)return 0;//入度为0的点 有0个 或超过1个,表示没有对应的正常字母,返回0
        ans[++p]=k;
        for(i=1;i<=m;i++)
            if(map[k][i])map[k][i]=0,rd[i]--;
    }
    return 1;
}
int main(){
    int i,j,len;
    char s_t[100];//字符串临时变量s_t
    memset(word,0,sizeof(word)),memset(rd,0,sizeof(rd)),memset(map,0,sizeof(map)),memset(s,0,sizeof(s));//初始化为0,这样就能确定该串的结束位置,为0
    scanf("%d",&n);
    for(i=1;i<=n+1;i++){
        scanf("%s",s_t+1),len=strlen(s_t+1);//此处写成 scanf("%s",s_t+1),len=strlen(s_t);
        for(j=1;j<=len;j++)s[i][++s[i][0]]=s_t[j]-'a'+1,word[s_t[j]-'a'+1]=1;//s[i][0] 存储 i串 字符串长度 //字母-数字 a-1 b-2 ......对应关系
    }
    for(i=1;i<=26;i++)
        if(word[i]==1)m++;//统计染毒字典中所含字母个数 正常
    for(i=1;i<n;i++)build(i,1);//请注意i<n,因build函数中,涉及i+1,故i<=n错误
    if(topsort()==0)printf("0");
    else
        for(i=1;i<=s[n+1][0];i++)
            for(j=1;j<=m;j++)
                if(s[n+1][i]==ans[j])printf("%c",j-1+'a');//ans[j] j对应 正常字母 ans[j]对应 染毒后的字母
    return 0;
}

2018-1-2 20:47 AC 该节内容

2018-1-2 20:47 AC 该章节内容

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值