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;
}
来源:https://blog.csdn.net/mrcrack/article/details/100972410