NOIP 考前 图论练习

时间:2023-03-09 14:55:07
NOIP 考前 图论练习

LJOJ 1500:

题目:http://www.docin.com/p-601990756.html

Sol:贪心,从叶子结点往上加入无法传递了,就需要建设。 Dfs返回的是到达叶子节点最多所要的能量,如果大于最大能量就需要建设放大器.

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
const int Maxn=;
using namespace std;
int head[Maxn],vis[Maxn],cnt,Ans,x,v,w,n,Power;
struct EDGE{int to,next,w;}edge[Maxn<<];
inline void Add(int u,int v,int w) {edge[cnt].to=v;edge[cnt].next=head[u];edge[cnt].w=w;head[u]=cnt++;}
inline int Max(int x,int y) {return x>y?x:y;}
int Dfs(int u)
{
int Res=; vis[u]=true;
for (int i=head[u];i!=-;i=edge[i].next)
if (!vis[edge[i].to])
{
int Tmp=Dfs(edge[i].to);
if (Tmp+edge[i].w>=Power) {if (edge[i].w>Res) Res=edge[i].w; Ans++;}
else if (Tmp+edge[i].w>Res) Res=Tmp+edge[i].w;
}
return Res;
}
int main()
{
scanf("%d",&n); int Mx=;
cnt=; memset(head,-,sizeof(head)); Ans=;
for (int i=;i<=n;i++)
{
scanf("%d",&x);
for (int j=;j<=x;j++)
{
scanf("%d%d",&v,&w);
Add(i,v,w),Add(v,i,w); Mx=Max(Mx,w);
}
}
scanf("%d",&Power);
if (Mx>=Power) {puts("No solution."); return ;}
Dfs();
printf("%d\n",Ans);
return ;
}

C++

LJOJ 1501:

题目描述
  出于最高安全性考虑,司令部采用了特殊的安全操作系统,该系统采用一个特殊的文件系统。在这个文件系统中所有磁盘空间都被分成了相同尺寸的N块,用整数1到N标识。每个文件占用磁盘上任意区域的一块或多块存储区,未被文件占用的存储块被认为是可使用的。如果文件存储在磁盘上自然连续的存储块中,则能被以最快的速度读出。   因为磁盘是匀速转动的,所以存取上面不同的存储块需要的时间也不同。读取磁盘开头处的存储块比读取磁盘结尾处的存储块快。根据以上现象,我们事先将文件按其存取频率的大小用整数1到K标识。按文件在磁盘上的最佳存储方法,1号文件将占用1,,…,S1的存储块,2号文件将占用S1+,S1+,...,S1+S2的存储块,以此类推(Si是被第i个文件占用的存储块的个数)。为了将文件以最佳形式存储在磁盘上,需要执行存储块移动操作。一个存储块移动操作包括从磁盘上读取一个被占用的存储块至内存并将它写入其它空的存储块,然后宣称前一个存储块被释放,后一个存储块被占用。   本程序的目的是通过执行最少次数的存储块移动操作,将文件按最佳方式存储到磁盘上,注意同一个文件的存储块在移动之后其相对次序不可改变。
输入
  每个磁盘说明的第一行包含两个用空格隔开的整数N和K,<=K<=N<=,接下来的K行每行说明一个文件,对第i个文件的说明是这样的:首先以整数Si开头,表示第i个文件的存储块数量,<=Si<=N-K,然后跟Si个整数,每个整数之间用空格隔开,表示该文件按自然顺序在磁盘上占用的存储块的标识。所有这些数都介于1和N之间,包括1和N。   一个磁盘说明中所有存储块的标识都是不同的,并且该盘至少有一个空的存储块。
输出
  对于每一个磁盘说明,只需输出一行句子"We need M move operations",M表示将文件按最佳方式存储到磁盘上所需进行的最少存储块移动操作次数。如果文件已按最佳方式存储,仅需输出“No optimization needed.”。
样例输入 样例输出
We need move operations.

题目

Sol:环需要有多余的一块进行交换,不然会覆盖

 #include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int cnt,mb[],n,k,p,x,v[];
int work(int x)
{
if (mb[x]==x) return ;
int p=x,t=x,tot=;v[p]=;
while ()
{
p=mb[p];
tot++;
if (p!=t && p!= && v[p]) return tot;
v[p]=;
if (p==) return tot-;
if (p==t) return tot+; }
}
int main()
{
scanf("%d%d",&n,&k);
for (int i=;i<=k;i++)
{
scanf("%d",&p);
for (int i=;i<=p;i++)
{
scanf("%d",&x);
mb[++cnt]=x;
}
}
int ans=;
for (int i=;i<=cnt;i++) if (!v[i]) ans+=work(i);
if (ans==) printf("No optimization needed.");
else printf("We need %d move operations.\n",ans);
}

C++

LJOJ 1502:

题目:删去一条边使得最短路最长

Sol:Dij后找到最短路,依次删去最短路上的边进行最短路即可

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#define Pa pair<int,int>
#define mp make_pair
using namespace std;
const int Inf=0x3f3f3f3f;
const int Maxn=;
const int Maxm=;
priority_queue<Pa,vector<Pa>,greater<Pa> > Q;
struct EDGE{int to,next,w;}edge[Maxm<<];
int head[Maxn],Dis[Maxn],vis[Maxn],n,m,u,v,w,cnt,Pre[Maxn],Ans,Ban,Ban_Edge[Maxn];
inline void Add(int u,int v,int w) {edge[cnt].to=v;edge[cnt].next=head[u];edge[cnt].w=w; head[u]=cnt++;}
int Dij(int Flag)
{
memset(vis,false,sizeof(vis));
for (int i=;i<=n;i++) Dis[i]=Inf;
Dis[]=; Q.push(mp(,));
while (!Q.empty())
{
int u=Q.top().second; Q.pop();
if (vis[u]) continue; vis[u]=true;
for (int i=head[u];i!=-;i=edge[i].next)
if (!vis[edge[i].to] && Dis[edge[i].to]>Dis[u]+edge[i].w && Ban!=i && Ban!=(i^))
{
Dis[edge[i].to]=Dis[u]+edge[i].w;
Q.push(mp(Dis[edge[i].to],edge[i].to));
if (Flag) Pre[edge[i].to]=u,Ban_Edge[edge[i].to]=i;
}
}
return Dis[n];
}
int main()
{
// freopen("c.in","r",stdin);
scanf("%d%d",&n,&m); memset(head,-,sizeof(head));
for (int i=;i<=m;i++) scanf("%d%d%d",&u,&v,&w),Add(u,v,w),Add(v,u,w);
Ban=Inf;
Dij(true); Ans=;
u=n;
while (true)
{
Ban=Ban_Edge[u];
int Tmp=Dij(false);
if (Tmp>Ans) Ans=Tmp;
u=Pre[u];
if (u==) break;
}
printf("%d\n",Ans);
return ; }

C++

LJOJ 1503

题目:经典网络流:Pig

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define Inf 1<<29
using namespace std;
const int Maxn=;
struct node
{
int to,next,w;
}edge[];
int cnt,Level[Maxn<<],pigs[Maxn<<],Q[],head[Maxn<<],n,m,Pre[Maxn<<],limit;
inline void Add(int u,int v,int w)
{
edge[cnt].to=v;
edge[cnt].next=head[u];
edge[cnt].w=w;
head[u]=cnt++;
edge[cnt].to=u;
edge[cnt].next=head[v];
edge[cnt].w=;//写成w
head[v]=cnt++;
} inline int Min(int a,int b){return a>b?b:a;}
bool Bfs()
{
memset(Level,-,sizeof(Level));
int l=,r=;
Q[]=;Level[]=;
while (l<=r)
{
int u=Q[l++];
for (int i=head[u];i!=-;i=edge[i].next)
if (Level[edge[i].to]==- && edge[i].w>)
{
Level[edge[i].to]=Level[u]+;
Q[++r]=edge[i].to;
}
}
if (Level[n+]>) return true;
return false;
} int Find(int u,int low)
{
if (u==n+) return low;
int tmp=;
for (int i=head[u];i!=-;i=edge[i].next)
{
int v=edge[i].to;
if (edge[i].w>
&& Level[v]==Level[u]+
&& (tmp=Find(v,Min(low,edge[i].w))))
{
edge[i].w-=tmp,edge[i^].w+=tmp;
return tmp;
}
}
Level[u]=-;
return ;
} int main()
{
scanf("%d%d",&m,&n);
memset(Pre,,sizeof(Pre));
memset(head,-,sizeof(head));
cnt=;
for (int i=;i<=m;i++) scanf("%d",&pigs[i]);
for (int i=;i<=n;i++)
{
int tn;
scanf("%d",&tn);
int sum=;
for (int j=;j<=tn;j++)
{
int x;
scanf("%d",&x);
if (Pre[x]==)
sum+=pigs[x]; else
Add(Pre[x],i,Inf);
Pre[x]=i;
}
if (sum!=) Add(,i,sum);
scanf("%d",&limit);
Add(i,n+,limit);
}
int ans=,temp;
while (Bfs())
while (temp=Find(,0x7fffffff))
ans+=temp;
printf("%d\n",ans);
return ;
}

C++

LJOJ 1504

题目: Codevs 3637

Sol:显然不可能形成三角形,假设每点左边的边小于右边的边,会矛盾,然后用Prim就可以了.

 #include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std; const int MAXN=;
const int INF=1e8; struct point{
int x,y;
point(){}
point(int x,int y):x(x),y(y){}
}; int n;
point p[MAXN];
double ans=,d[MAXN];
bool vis[MAXN]={}; double dist(point a,point b){
return sqrt((double)(a.x-b.x)*(double)(a.x-b.x)+(double)(a.y-b.y)*(double)(a.y-b.y));
} int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d%d",&p[i].x,&p[i].y);
d[i]=INF;
}
d[]=;
for(int k,i=;i<=n;i++){
double mi=INF;
for(int j=;j<=n;j++){
if(!vis[j]&&mi>d[j]){
mi=d[j];
k=j;
}
}
if(mi==INF)break;
vis[k]=;
ans+=mi;
for(int j=;j<=n;j++){
if(!vis[j]){
d[j]=min(d[j],dist(p[j],p[k]));
}
}
}
printf("%.2lf\n",ans);
return ;
}

C++

LJOJ 1505

 题目描述
  在这个繁忙的社会中,我们往往不再去选择最短的道路,而是选择最快的路线。开车时每条道路的限速成为最关键的问题。不幸的是,有一些限速的标志丢失了,因此你无法得知应该开多快。一种可以辩解的解决方案是,按照原来的速度行驶。你的任务是计算两地间的最快路线。   你将获得一现代化城市的道路交通信息。为了使问题简化,地图只包括路口和道路。每条道路是有向的,只连接了两条道路,并且最多只有一块限速标志,位于路的起点。两地A和B,最多只有一条道路从A连接到B。你可以假设加速能够在瞬间完成并且不会有交通堵塞等情况会影响你。当然,你的车速不能超过当前的速度限制。
输入
  输入文件SPEED.IN的第一行是3个整数 N,M和D,<=N<=,表示道路的数目,用0..N-1标记。M是道路的总数,D表示你的目的地。接下来的M行,每行描述一条道路,每行有4个整数A ( <= A < N), B ( <= B < N), V ( <= V <= ) and L (<= L <= ),这条路是从A到B的,速度限制是V,长度为L。如果V是0,表示这条路的限速未知。如果V不为0,则经过该路的时间T=L/V。否则T = L / Vold,Vold是你到达该路口前的速度。开始时你位于0点,并且速度为70。
输出
  输出文件SPEED.OUT仅一行整数,表示从0到D经过的城市。输出的顺序必须按照你经过这些城市的顺序,以0开始,以D结束。仅有一条最快路线。
样例输入 样例输出 提示
The required time is in this case , units. 来源

题目

Sol:Bfs记录上次的速度

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#define fi first
#define se second
#define Pa pair<int,int>
#define mp make_pair
#define pb push_back
using namespace std;
int n,m,T,u,v,Speed,Len,vis[][];
double f[][];
vector<pair<int,pair<int,int> > > E[];
vector<int> Ret;
Pa Pre[][];
int main()
{
scanf("%d%d%d",&n,&m,&T);
for (int i=;i<=m;i++)
{
scanf("%d%d%d%d",&u,&v,&Speed,&Len);
E[u].pb(mp(v,mp(Speed,Len)));
}
queue<Pa> Q;
memset(f,0x7f,sizeof(f));
memset(vis,false,sizeof(vis));
f[][]=,vis[][]=true;
Q.push(mp(,));
while(!Q.empty())
{
Pa u=Q.front(); vis[u.fi][u.se]=; Q.pop();
for (int i=;i<E[u.fi].size();i++)
{
int v=E[u.fi][i].fi,Race=u.se;
if (E[u.fi][i].se.fi!=) Race=E[u.fi][i].se.fi;
if (f[u.fi][u.se]+E[u.fi][i].se.se*1.0/Race<f[v][Race])
{
f[v][Race]=f[u.fi][u.se]+E[u.fi][i].se.se*1.0/Race;
Pre[v][Race]=u;
if (!vis[v][Race])
{
vis[v][Race]=true;
Q.push(mp(v,Race));
}
}
}
}
double Ans=1e30; Pa Opt;
for (int i=;i<=;i++) if (f[T][i]<Ans) Ans=f[T][i],Opt=mp(T,i);
while (Opt.fi!=) Ret.pb(Opt.fi),Opt=Pre[Opt.fi][Opt.se];
printf("0 ");
for (int i=Ret.size()-;i>;i--) printf("%d ",Ret[i]);
printf("%d\n",Ret[]);
}

C++

LJOJ 1506

 题目描述
  你的任务是把一些人分成两组,使得:  •每个人都被分到其中一组;  •每个组都至少有一个人;  •一组中的每个人都认识其他同组成员;  •两组的成员人数尽量接近。  这个问题可能有多个解决方案,你只要输出任意一个即可,或者输出这样的分组法不存在。 输入
  为了简单起见,所有的人都用一个整数标记,每个人号码不同,从1到N。  输入文件的第一行包括一个整数N(≤N≤),N就是需要分组的总人数;接下来的N行对应了这N个人,按每个人号码的升序排列,每一行给出了一串号码Aij (≤Aij≤N,Aij≠i),代表了第i个人所认识的人的号码,号码之间用空格隔开,并以一个“”结束。 输出
  如果分组方法不存在,就输出信息“No solution”(输出时无需加引号)至输出文件;  否则输出两组人数的差的绝对值 样例输入 样例输出

题目

Sol:两个不认识的人肯定不能在一组里,那么就可以相互染色,然后可以分成两块。

 #include <cstdio>
const int Inf=0x3f3f3f3f;
int n,Cnt,Ans;
bool G[][],Tmp[][],F[][];
int vis[],c[],a[],b[],x;
inline int Abs(int x) {return x<?-x:x;}
bool Dfs(int u,int Color)
{
vis[u]=true; c[u]=Color;
for (int i=;i<=n;i++)
{
if (!G[u][i]) continue;
if (c[i]==Color) return false;
else if (c[i]== && !Dfs(i,-Color)) return false;
}
return true;
}
bool Work()
{
for (int i=;i<=n;i++)
{
if (vis[i]) continue;
if (!Dfs(i,i)) return false;
Cnt++;
for (int j=;j<=n;j++)
{
if (Abs(c[j])!=i) continue;
a[Cnt]+=(c[j]==i);
b[Cnt]+=(c[j]==-i);
}
}
return true;
}
int main()
{
scanf("%d",&n);
for (int i=;i<=n;i++)
{
while (true)
{
scanf("%d",&x); if (x==) break;
Tmp[i][x]=true;
}
}
for (int i=;i<=n;i++) for (int j=i+;j<=n;j++) if (!(Tmp[i][j]&&Tmp[j][i])) G[i][j]=G[j][i]=true;
if (!Work()) {puts("No solution"); return ;}
F[][]=;
for (int Op=;Op<=Cnt;Op++)
{
for (int i=n;i>=;i--) for (int j=n;j>=;j--)
{
if (i>=a[Op] && j>=b[Op] && F[i-a[Op]][j-b[Op]]) F[i][j]=true;
if (i>=b[Op] && j>=a[Op] && F[i-b[Op]][j-a[Op]]) F[i][j]=true;
}
}
Ans=;
for (int i=;i<=n;i++)
{
int j=n-i;
if (Abs(i-j)<Ans && F[i][j]) Ans=Abs(i-j);
}
printf("%d\n",Ans);
return ;
}

C++

LJOJ 1507

题目:RQNOJ P319

Sol: 可以计算出移动之后的Ans减少了Size[u]又增加加了Tot-Size[u],Dfs找到不能移动为止。

 #include <cstdio>
#include <cstring>
#define LL long long
const LL Maxn=;
struct EDGE{LL to,next;}edge[Maxn<<];
LL head[Maxn],Sum[Maxn],Dep[Maxn],Ans,n,l,r,w[Maxn],cnt,Tot;
inline void Add(LL u,LL v)
{edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
void Dfs(LL u,LL fa)
{
Sum[u]=w[u];
for (LL i=head[u];i!=-;i=edge[i].next)
{
if (edge[i].to==fa) continue;
Dep[edge[i].to]=Dep[u]+;
Dfs(edge[i].to,u);
Sum[u]+=Sum[edge[i].to];
}
}
void Find(LL u,LL fa)
{
for (LL i=head[u];i!=-;i=edge[i].next)
{
if (edge[i].to==fa) continue;
if (Tot-*Sum[edge[i].to]<)
{
Ans+=Tot-*Sum[edge[i].to];
Find(edge[i].to,u);
}
}
}
int main()
{
scanf("%lld",&n); memset(head,-,sizeof(head));
for (LL i=;i<=n;i++)
{
scanf("%lld%lld%lld",&w[i],&l,&r);
if (l) Add(i,l),Add(l,i);
if (r) Add(i,r),Add(r,i);
Tot+=w[i];
}
Dep[]=;
Dfs(,);
for (LL i=;i<=n;i++) Ans+=(Dep[i]-Dep[])*w[i];
Find(,);
printf("%lld\n",Ans);
return ;
}

C++

LJOJ 1508

题目:POJ 3694

Sol:求割点当且仅当 Low[v]>=Dfn[u]

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn=;
const int Maxm=;
struct EDGE{int to,next;}edge[Maxm];
int head[Maxn],Dfn[Maxn],Low[Maxn],Root,Son,u,v,n,cnt,Stamp;
bool Cut[Maxn];
inline int Min(int x,int y) {return x>y?y:x;}
inline void Add(int u,int v) {edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
void Tarjan(int u)
{
Dfn[u]=Low[u]=++Stamp;
for (int i=head[u];i!=-;i=edge[i].next)
{
int v=edge[i].to;
if (!Dfn[v])
{
Tarjan(v);
if (u==Root) Son++;
else
{
Low[u]=Min(Low[u],Low[v]);
if (Dfn[u]<=Low[v]) Cut[u]=true;
}
} else
Low[u]=Min(Low[u],Dfn[v]);
}
}
int main()
{
while (scanf("%d",&n)!=EOF && n!=)
{
memset(head,-,sizeof(head));
memset(Dfn,,sizeof(Dfn));
memset(Low,,sizeof(Low));
memset(Cut,false,sizeof(Cut)); Stamp=; cnt=;
while (scanf("%d",&u)!=EOF && u!=)
while (getchar()!='\n')
{
scanf("%d",&v);
Add(u,v),Add(v,u);
}
Root=,Son=;
Tarjan();
int Ans=;
if (Son>) Ans=;
for (int i=;i<=n;i++) if (Cut[i]) Ans++;
printf("%d\n",Ans);
}
return ;
}

C++

LJOJ 1509

题目:POJ 3177

Sol:Tarjan缩点之后找到桥的个数/2

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxn=;
const int Maxm=;
struct EDGE{int to,next;}edge[Maxm<<];
int head[Maxn],Dfn[Maxn],Low[Maxn],vis[Maxn],n,m,u,v,cnt,Top,Stack[Maxn],d[Maxn],Scc,Stamp,Belong[Maxn],Ans;
inline void Add(int u,int v) {edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
inline int Min(int x,int y) {return x>y?y:x;}
void Tarjan(int u,int fa)
{
Dfn[u]=Low[u]=++Stamp; Stack[++Top]=u; vis[u]=true;
for (int i=head[u];i!=-;i=edge[i].next)
{
int v=edge[i].to;
if (v==fa) continue;
if (!Dfn[v] && !vis[v])
{
Tarjan(v,u);
Low[u]=Min(Low[u],Low[v]);
} else if (vis[v]) Low[u]=Min(Low[u],Dfn[v]);
}
if (Dfn[u]==Low[u])
{
Scc++;
while (Stack[Top]!=u && Top>=) Belong[Stack[Top--]]=Scc;
Belong[Stack[Top--]]=Scc;
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(head,-,sizeof(head));
for (int i=;i<=m;i++)
{
scanf("%d%d",&u,&v);
Add(u,v),Add(v,u);
}
Tarjan(,);
for (int u=;u<=n;u++)
{
for (int i=head[u];i!=-;i=edge[i].next)
if (Belong[u]!=Belong[edge[i].to]) d[Belong[edge[i].to]]++;
} for (int i=;i<=Scc;i++) if (d[i]==) Ans++;
printf("%d\n",(Ans+)>>);
return ;
}

C++

LJOJ 1510

题目: BZOJ 1596

Sol: 树形DP

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxn=;
const int Inf=;
struct Node{int to,next;}edge[Maxn<<];
int F[Maxn][],cnt,n,u,v,head[Maxn];
bool vis[Maxn];
inline int Max(int x,int y) {return x>y?x:y;}
inline int Min(int x,int y) {return x>y?y:x;}
inline int Min3(int x,int y,int z) {return Min(x,Min(y,z));}
inline void Add(int u,int v)
{edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
void Dp(int u,int fa)
{
F[u][]=; F[u][]=; int Mx=-Inf;
for (int i=head[u];i!=-;i=edge[i].next)
{
if (fa==edge[i].to) continue;
Dp(edge[i].to,u);
F[u][]+=Min3(F[edge[i].to][],F[edge[i].to][],F[edge[i].to][]);
F[u][]+=Min(F[edge[i].to][],F[edge[i].to][]);
Mx=Max(Mx,Min(F[edge[i].to][],F[edge[i].to][])-F[edge[i].to][]);
F[u][]+=F[edge[i].to][];
}
F[u][]-=Mx;
}
int main()
{
scanf("%d",&n);
memset(head,-,sizeof(head));
for (int i=;i<n;i++) scanf("%d%d",&u,&v),Add(u,v),Add(v,u); Dp(,);
printf("%d\n",Min(F[][],F[][]));
return ;
}

C++

POJ 1274

二分图匹配

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
const int Maxn=;
int n,m,G[Maxn][Maxn],Link[Maxn],vis[Maxn],num,tmp;
bool Dfs(int u)
{
for (int i=;i<=m;i++)
if (G[u][i] && !vis[i])
{
vis[i]=true;
if (Link[i]==- || Dfs(Link[i]))
{
Link[i]=u;
return true;
}
}
return false;
}
int Hungary()
{
int Ret=;
memset(Link,-,sizeof(Link));
for (int i=;i<=n;i++)
{
for (int j=;j<=m;j++) vis[j]=false;
if (Dfs(i)) Ret++;
}
return Ret;
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(G,false,sizeof(G));
for (int i=;i<=n;i++)
{
scanf("%d",&num);
for (int j=;j<=num;j++)
{
scanf("%d",&tmp);
G[i][tmp]=true;
}
}
printf("%d\n",Hungary());
}
return ;
}

POJ 1274

BZOJ 3436

差分约束系统

a比b至少多c -> Add(a,b,-c)

a比b至多多c -> b比a至少多-c  Add(b,a,c);

a比b相等 ->Add(a,b,0)

加入满足所有要求则无负环

 #include <cstdio>
#include <cstring>
const int Inf=0x3f3f3f3f;
const int Maxn=;
struct EDGE{int to,next,w;}edge[Maxn<<];
int head[Maxn<<],n,m,vis[Maxn],Type,x,y,z,Dis[Maxn],flag,cnt;
inline void Add(int u,int v,int w)
{edge[cnt].to=v;edge[cnt].next=head[u];edge[cnt].w=w;head[u]=cnt++;}
void Spfa(int u)
{
vis[u]=true;
for (int i=head[u];i!=-;i=edge[i].next)
if (Dis[edge[i].to]>Dis[u]+edge[i].w)
{
if (vis[edge[i].to])
{
flag=true;
break;
}
Dis[edge[i].to]=Dis[u]+edge[i].w;
Spfa(edge[i].to);
}
vis[u]=false;
} int main()
{ // freopen("c.in","r",stdin);
scanf("%d%d",&n,&m);
memset(head,-,sizeof(head));
for (int i=;i<=m;i++)
{
scanf("%d",&Type);
if (Type==)
{
scanf("%d%d%d",&x,&y,&z);
Add(x,y,-z);
}
if (Type==)
{
scanf("%d%d%d",&x,&y,&z);
Add(y,x,z);
}
if (Type==)
{
scanf("%d%d",&x,&y);
Add(x,y,);
}
}
for (int i=;i<=n;i++)
{
Dis[i]=;
Spfa(i);
}
if (flag) puts("No"); else puts("Yes");
return ;
}

BZOJ 3436

BZOJ 2068

拓扑排序,把所有入度为0的节点u加入对列,那么a[u]就是可以选择。再把a[a[u]]为0的加入

剩下的还有环就是环内总的节点数/2就可以了

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxn=;
int a[Maxn],In[Maxn],Q[Maxn],n,Ans;
bool vis[Maxn];
void Work()
{
int l=,r=;
for (int i=;i<=n;i++) if (In[i]==) Q[++r]=i,vis[i]=true;
while (l<=r)
{
int u=Q[l++];
if (!vis[a[u]])
{
Ans++,vis[a[u]]=true,In[a[a[u]]]--;
if (!vis[a[a[u]]] && In[a[a[u]]]==)
{
vis[a[a[u]]]=true;
Q[++r]=a[a[u]];
}
}
}
for (int i=;i<=n;i++)
if (!vis[i])
{
int t=i,Ret=;
while (!vis[t]) Ret++,vis[t]=true,t=a[t];
Ans+=(Ret>>);
}
}
int main()
{ scanf("%d",&n);
for (int i=;i<=n;i++)
{
scanf("%d",&a[i]);
In[a[i]]++;
}
Work();
printf("%d\n",Ans);
return ;
}

BZOJ 2068

BZOJ 1734 Floyd求最小环,就是多了一层对于已经得知道最短路,然后从后面的值开始Floyd

 #include <iostream>
#include <cstring>
#include <cstdio> using namespace std;
const int N=;
const int Inf=; int G[N][N],F[N][N],u,v,w;
int Road[N][N],Path[N]; int m,n,cnt,Ans; void Record(int s,int t)
{
if (Road[s][t])
{
Record(s,Road[s][t]);
Record(Road[s][t],t);
}
else Path[cnt++]=t;
} void Floyd()
{
Ans=Inf;
for (int k=;k<=n;k++)
{
for (int i=;i<k;i++)
for (int j=i+;j<k;j++)
if (Ans>F[i][j]+G[i][k]+G[k][j])
{
Ans=F[i][j]+G[i][k]+G[k][j];
cnt=;
Path[cnt++]=i;
Record(i,j);
Path[cnt++]=k;
}
for (int i=;i<=n;i++)
for (int j=;j<=n;j++)
if (F[i][j]>F[i][k]+F[k][j])
{
F[i][j]=F[i][k]+F[k][j];
Road[i][j]=k;
}
}
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
for (int i=;i<=n;i++)
for (int j=;j<=n;j++) F[i][j]=Inf,Road[i][j]=;
for (int i=;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
if (w<F[u][v]) F[u][v]=F[v][u]=w;
}
for (int i=;i<=n;i++)
for (int j=;j<=n;j++) G[i][j]=F[i][j];
Floyd();
if (Ans==Inf) puts("No solution.");
else
{
printf("%d",Path[]);
for (int i=;i<cnt;i++)
printf(" %d",Path[i]);
puts("");
}
}
return ;
}

BZOJ 1734