题解 AT696 【グラフ】
xtx1092515503 · · 题解
说一种虽然不是正解但确实是对的的方法吧。
方法来自于ET2006神仙跟我偶然扯到的神仙算法。
翻译:给定一张有向图,我们在图上找出两条路径
思路:
路径问题,首选SCC(强连通分量)缩点,因为同一块强连通分量里的所有点肯定是互相可达的。
缩完点后,我们得到一张DAG。
现在问题转换为:给你一张带权DAG,找出两条路径,使得它们并集的权值最大。
正解是DP(毕竟这是DP Contest嘛)
不过我们可以发现,这个模型有点像费用流!我们拆个点限制每个点的权值只会被计入一次,然后建立一个伪源点和伪汇点
具体的说,我们将每个点
对于缩点后的图上每一条边
对于缩点后的图上每个点
最后,连边
答案即为最大费用最大流。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,col[310],c,sz[310],s,t;
namespace SCC{
int tot,dfn[310],low[310],head[310],cnt;
struct node{
int to,next;
}edge[200100];
void ae(int u,int v){
// cout<<u<<" "<<v<<endl;
edge[cnt].next=head[u],edge[cnt].to=v,head[u]=cnt++;
}
stack<int>stk;
void Tarjan(int x){
dfn[x]=low[x]=++tot,stk.push(x);
for(int i=head[x];i!=-1;i=edge[i].next){
if(!dfn[edge[i].to])Tarjan(edge[i].to),low[x]=min(low[x],low[edge[i].to]);
if(!col[edge[i].to])low[x]=min(low[x],dfn[edge[i].to]);
}
if(low[x]<dfn[x])return;
c++;
while(stk.top()!=x)col[stk.top()]=c,stk.pop(),sz[c]++;
col[stk.top()]=c,stk.pop(),sz[c]++;
}
}
namespace MCMF{
const int N=1000,M=200000;
int head[N],cnt,dis[N],fr[N],id[N],S,T,cost;
struct node{
int to,next,val,cost;
}edge[M];
void ae(int u,int v,int w,int c){
// printf("%d %d %d %d\n",u,v,w,c);
edge[cnt].cost=c,edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
edge[cnt].cost=-c,edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=0,head[v]=cnt++;
}
queue<int>q;
bool in[N];
bool SPFA(){
memset(dis,0x80,sizeof(dis)),dis[S]=0,q.push(S),in[S]=true;
while(!q.empty()){
int x=q.front();q.pop(),in[x]=false;
// printf("%d\n",x);
for(int i=head[x];i!=-1;i=edge[i].next){
if(!edge[i].val)continue;
if(dis[edge[i].to]<dis[x]+edge[i].cost){
dis[edge[i].to]=dis[x]+edge[i].cost,fr[edge[i].to]=x,id[edge[i].to]=i;
if(!in[edge[i].to])in[edge[i].to]=true,q.push(edge[i].to);
}
}
}
if(dis[T]==dis[0])return false;
int x=T,mn=0x3f3f3f3f;
while(x!=S)mn=min(mn,edge[id[x]].val),x=fr[x];
cost+=dis[T]*mn,x=T;
while(x!=S)edge[id[x]].val-=mn,edge[id[x]^1].val+=mn,x=fr[x];
return true;
}
}
int main(){
scanf("%d",&n),memset(SCC::head,-1,sizeof(SCC::head)),memset(MCMF::head,-1,sizeof(MCMF::head));
for(int i=1;i<=n;i++)for(int j=1,x;j<=n;j++){
scanf("%d",&x);
if(x)SCC::ae(i,j);
}
for(int i=1;i<=n;i++)if(!SCC::dfn[i])SCC::Tarjan(i);
// for(int i=1;i<=n;i++)printf("%d\n",col[i]);
MCMF::S=c*2+1,MCMF::T=c*2+2,s=c*2+3,t=c*2+4;
for(int i=1;i<=n;i++)for(int j=SCC::head[i];j!=-1;j=SCC::edge[j].next){
if(col[SCC::edge[j].to]==col[i])continue;
MCMF::ae(col[i]+c,col[SCC::edge[j].to],2,0);
}
for(int i=1;i<=c;i++)MCMF::ae(s,i,2,0);
for(int i=1;i<=c;i++)MCMF::ae(i+c,t,2,0);
MCMF::ae(MCMF::S,s,2,0),MCMF::ae(t,MCMF::T,2,0);
for(int i=1;i<=c;i++)MCMF::ae(i,i+c,1,sz[i]),MCMF::ae(i,i+c,1,0);
while(MCMF::SPFA());
printf("%d\n",MCMF::cost);
return 0;
}