bzoj 2208 //2208: [Jsoi2010]连通数

bzoj 2208   //2208: [Jsoi2010]连通数   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=2208

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

//2208: [Jsoi2010]连通数
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=2208
//第一个思路,Floyd算法,求最短路径,算法的时间复杂度O(n^3),看了数据范围 N不超过2000,只好作罢 
//统计 连通数 时,采用O(n^2)算法即可 
//问题是,如何确认2点连通,这个算法的时间复杂度,如何降下来。 
//多点的连通问题。
//强连通分量及缩点tarjan算法解析https://blog.csdn.net/acmmmm/article/details/16361033可看此文,手绘图。

//初探tarjan算法(求强连通分量)https://www.luogu.org/blog/styx-ferryman/chu-tan-tarjan-suan-fa-qiu-qiang-lian-tong-fen-liang-post此文值得一看
//强连通分量及缩点tarjan算法解析https://blog.csdn.net/sentimental_dog/article/details/53790582可看此文2019-6-13

 
//该题可这样理解,到达1点的数目为1
//到达2点的数目为2,到达3点的数目为3
//到达4点的数目为4,到达5点的数目为4
//总数目为1+2+3+4+4=14
//准备采用邻接矩阵
//2000*2000=4*10^6,故int不溢出
//对着题目造了一组样例,发现输出数据不对,仔细排查,发现是造的数据,格式不对
//马上修改,输入数据,
//v=stack[top],color[v]=c,sum[c]++,in_stack[v]=0;//此处写成 v=stack[top],color[v]=c,sum[c]++;漏了in_stack[v]=0;
//if(str[j]=='1')m++,a[m]=i,b[m]=j,add_edge(i,j);//此处写成 if(str[j]=='1')m++,a[m]=i,b[m]=j,add_edge(i,j),in_degree[j]++;
//add_edge(color[a[i]],color[b[i]]),in_degree[color[b[i]]]++;//此处写成 add_edge(color[a[i]],color[b[i]]);
//修改,自个造的样例通过,数据如下
//输入
5
01100
00101
00011
00000
00000
//输出
14

//Tarjan+缩点+拓扑排序
//之后的部分,还得学习
 
 //引入 状态压缩。
//样例通过,提交AC。2019-6-18
//此文思路不错https://blog.csdn.net/PoPoQQQ/article/details/40044085
#include <cstdio>
#include <cstring>
#include <bitset>
#include <iostream>
#define maxn 2010
using namespace std;
int cnt=0,head[maxn],m=0,n,a[maxn*maxn],b[maxn*maxn];//m记录边数
int in_stack[maxn],stack[maxn],top=0,dfn[maxn],low[maxn],tag=0;
int color[maxn],c=0,sum[maxn],q[maxn],in_degree[maxn],dp[maxn];//sum[i]缩点
char str[maxn];
bitset<maxn> line[maxn];
struct edge{
    int to,next;
}e[maxn*maxn];
void add_edge(int u,int v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int min(int a,int b){
    return a<b?a:b;
}
int max(int a,int b){
    return a>b?a:b;
}
void Tarjan(int u){
    int b,v;
    dfn[u]=low[u]=++tag;
    top++,stack[top]=u,in_stack[u]=1;
    b=head[u];
    while(b){
        v=e[b].to;
        if(!dfn[v]){
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(in_stack[v])low[u]=min(low[u],low[v]);
        b=e[b].next;
    }
    if(dfn[u]==low[u]){
        c++;
        do{
            v=stack[top],color[v]=c,sum[c]++,in_stack[v]=0;//此处写成 v=stack[top],color[v]=c,sum[c]++;漏了in_stack[v]=0;
        }while(stack[top--]!=u);
    }
}
void top_sort(){
    int h,t,b,u,v,i;
    h=t=1;
    for(i=1;i<=c;i++)dp[i]=sum[i];
    for(i=1;i<=n;i++)
        if(!in_degree[i])q[t]=i,t++;
    while(h<t){
        u=q[h];
        b=head[u];
        while(b){
            v=e[b].to;
            line[v]|=line[u];//状压
            in_degree[v]--;
            if(!in_degree[v])q[t]=v,t++;
            b=e[b].next;
        }
        h++;
    }
}
int main(){
    int i,j,ans=0;
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn)),memset(low,0,sizeof(low)),memset(in_stack,0,sizeof(in_stack));
    memset(sum,0,sizeof(sum)),memset(in_degree,0,sizeof(in_degree));;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%s",str+1);
        for(j=1;j<=n;j++)
            if(str[j]=='1')m++,a[m]=i,b[m]=j,add_edge(i,j);//此处写成 if(str[j]=='1')m++,a[m]=i,b[m]=j,add_edge(i,j),in_degree[j]++;
    }
    for(i=1;i<=n;i++)
        if(!dfn[i])Tarjan(i);
    memset(head,0,sizeof(head)),cnt=0;//初始化,开始缩点处理
    for(i=1;i<=m;i++)
        if(color[a[i]]!=color[b[i]])
            add_edge(color[a[i]],color[b[i]]),in_degree[color[b[i]]]++;//此处写成 add_edge(color[a[i]],color[b[i]]);
    for(i=1;i<=c;i++)line[i][i]=1;
    top_sort();
    for(i=1;i<=c;i++)
        for(j=1;j<=c;j++)
            if(line[i][j])ans+=sum[i]*sum[j];
    printf("%d\n",ans);
    return 0;
}

 


// P3387 【模板】缩点   // 在线测评地址https://www.luogu.org/problemnew/show/P3387

// P3387 【模板】缩点
// 在线测评地址https://www.luogu.org/problemnew/show/P3387
//样例通过,提交40分,测试点1,3-5,7,9WA.2019-6-15 20:40
//重新读题,发现 只算了 强联通分量的最大值
//而题意是指 路径经过的点权值之和最大 最大强联通分量 可能只是 路径上的一员。
//偏爱https://blog.csdn.net/qq_38234381/article/details/81783043此文代码
//样例通过,提交0分,测试点1,3WA,测试点2,4-10RE。
//重读代码,发现测试语句未删除。
//删除测试代码。提交20分,测试点2,4-10RE.2019-6-16 11:09
//仔细想了想程序数据的流动,发现//此处写成a[maxn],b[maxn]
//修改,a[maxm],b[maxm],提交AC。2019-6-16 11:53
#include <stdio.h>
#include <string.h>
#define maxn 10010
#define maxm 100010
struct edge{
    int to,next;
}e[maxm];
int dfn[maxn],low[maxn],w[maxn],stack[maxn],top=0,in_stack[maxn],tag=0,dp[maxn],q[maxn];
int n,m,cnt=0,head[maxn],c=0,color[maxn],ans=0,a[maxm],b[maxm],sum[maxn],in_degree[maxn];//此处写成a[maxn],b[maxn]//in_degree[maxn]入度
int min(int a,int b){
    return a<b?a:b;
}
int max(int a,int b){
    return a>b?a:b;
}
void add_edge(int u,int v){//临接表存储
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void Tarjan(int u){
    int b,v;
    dfn[u]=low[u]=++tag;
    stack[++top]=u,in_stack[u]=1;//漏了此行代码
    b=head[u];
    while(b){
        v=e[b].to;
        if(!dfn[v]){
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(in_stack[v])low[u]=min(low[u],low[v]);
        b=e[b].next;
    }
    if(dfn[u]==low[u]){
        c++;
        do{
            v=stack[top],color[v]=c,in_stack[v]=0,sum[c]+=w[v];
        }while(stack[top--]!=u);
    }
}
void top_sort(){//拓扑排序
    int i,h,t,u,v,b;
    h=t=1;
    for(i=1;i<=c;i++)dp[i]=sum[i];
    for(i=1;i<=c;i++)
        if(!in_degree[i])
            q[t]=i,t++;
    while(h<t){
        u=q[h];
        b=head[u];
        while(b){
            v=e[b].to;
            if(dp[v]<dp[u]+sum[v])dp[v]=dp[u]+sum[v];//此处写成if(dp[v]<dp[u]+dp[v])dp[v]=dp[u]+dp[v];
            in_degree[v]--;
            if(!in_degree[v])q[t]=v,t++;
            b=e[b].next;
        }
        h++;
    }
}
int main(){
    int i;
    memset(dfn,0,sizeof(dfn)),memset(low,0,sizeof(low)),memset(head,0,sizeof(head)),memset(in_stack,0,sizeof(in_stack));
    memset(color,0,sizeof(color)),memset(sum,0,sizeof(sum));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&w[i]);
    for(i=1;i<=m;i++)scanf("%d%d",&a[i],&b[i]),add_edge(a[i],b[i]);//此处写成for(i=1;i<=n;i++)
    for(i=1;i<=n;i++)
        if(!dfn[i])Tarjan(i);
    memset(head,0,sizeof(head)),cnt=0,memset(in_degree,0,sizeof(in_degree));//重新构图,初始化
    for(i=1;i<=m;i++)
        if(color[a[i]]!=color[b[i]])//缩点
            add_edge(color[a[i]],color[b[i]]),in_degree[color[b[i]]]++;
    top_sort();
    for(i=1;i<=c;i++)
        ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值