给定DAG,每次从任一点出发,任一点结束,求至少遍历每个点一次的最少操作次数。 n ≤ 100 n\leq 100 n≤100。
根据题意想思路,要求每条边至少走过一边,肯定是可以用上下界网络流来做,这里给出一种更考思维的做法。
首先肯定希望每走一次走到的新点最多,这就有一种费用流的感觉。具体实现方法:对原图中每一条原路径改成两条流建边,第一条 u ⟶ 1 v u\stackrel{1}{\longrightarrow}v u⟶1v ,第二条 u ⟶ I N F v u\stackrel{INF}{\longrightarrow}v u⟶INFv ,分别的费用是1和0。
解释一下,每一个点可以计算一次答案(第一次清理雪道),其他时候不计入答案,但可以走过去。
然后就是给每一个点连上源点汇点,流量 I N F INF INF ,费用0,表示清理工作可以在任意时候开始结束。
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
int k,n,head[105],ecnt=1,s,t;
struct edge{
int nxt,to,from,w,c;
}e[20005];
queue <int> q;
int dis[105],pre[105],cntc[105],bj[105],incf[105],ans;
void adde(int u,int v,int w,int c){
e[++ecnt].nxt=head[u];
e[ecnt].c=c;
e[ecnt].w=w;
e[ecnt].from=u;
e[ecnt].to=v;
head[u]=ecnt;
}
bool spfa(){
while(!q.empty())
q.pop();
memset(bj,0,sizeof(bj));
for(int i=0;i<=n+1;++i)
dis[i]=-inf;
bj[s]=1;
incf[s]=inf;
dis[s]=0;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
bj[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].w>0 && e[i].c+dis[u]>dis[v]){
dis[v]=dis[u]+e[i].c;
incf[v]=min(incf[u],e[i].w);
pre[v]=i;
if(bj[v]==0){
bj[v]=1;
q.push(v);
}
}
}
}
if(dis[t]<=0)
return 0;
int x=t;
while(s!=x){
int y=pre[x];
e[y].w-=incf[t];
e[y^1].w+=incf[t];
x=e[y].from;
}
ans++;
return 1;
}
int main(){
scanf("%d",&n);
s=0,t=n+1;
for(int i=1;i<=n;++i){
int mi;
scanf("%d",&mi);
for(int j=1;j<=mi;++j){
int aij;
scanf("%d",&aij);
cntc[aij]++;
adde(i,aij,1,1);
adde(aij,i,0,-1);
adde(i,aij,inf,0);
adde(i,aij,0,0);
}
}
for(int i=1;i<=n;++i){
adde(s,i,inf,0);
adde(i,s,0,0);
adde(i,t,inf,0);
adde(t,i,0,0);
}
while(spfa());
printf("%d",ans);
return 0;
}