zoj3408 Gao

//这题Gao死我了!!!

Gao

题意:Gao这个函数可以用来算出有向图中从0到达某点的最短路径条数,用Gao算出0到所有点的最短路径条数了,现问有多少条路径经过点v。输出答案的后10位

解析:首先,这题有10000个点50000条边,对每点求最短路径是不可能的。观察最短路径以后我们可以知道,一个点v在一条最短路径里最多出现1次,要么出现在路径的末尾,要么出现在路径的中间,对于出现在末尾的最短路径数我们定义为cnt1[v],而我们在计算0点到所有点的最短路径过程中是可以记录cnt1[v]的:

 

if(dis[u]+w<dis[v])                  //u->v权值为w
{
	dis[v]=dis[u]+w;
	cnt1[v]=cnt1[u];
	tmp.v=v;tmp.dis=dis[v];
	que.push(tmp);
}
else if(dis[u]+w==dis[v])
{	cnt1[v]+=cnt1[u];
	cnt1[v]%=MOD;
}

 

至于v出现在最短路径的中间的情况,定义cnt2[v]表示最短路径中从v出发的路径条数,那么根据组合数学定义,v出现在最短路中间的最短路径条数=cnt1[v]*cnt2[v]。cnt2[v]可以用Calc(v)进行递归计算(貌似也可以叫树形DP,好像还可以拓扑排序以后之间DP),假如有t1,t2,t3...tn与v邻接且v->t1,v->t2,v->t3...,v->tn都在最短路径上,那么同样根据组合数学加法原理有cnt2[v]=cnt2[t1]+cnt2[t2]+...cnt[tn]。因为t1,t2,tn都在最短路径上,所以cnt2[v]+=n;

最终ans[v]=cnt1[v]+cnt1[v]*cnt2[v]。

这题比较阴险的地方在于答案要保留后10位,而进行乘法的过程中会超long long(事实证明,在计算cnt1的过程中也会超long long,在这地方无限WA T_T)要用到布斯(Booth)乘法:

 

long long Mul(long long lhs,long long rhs)
{
	long long lhs2=lhs%100000;
	long long rhs2=rhs%100000;
	return ((lhs/100000*rhs2+rhs/100000*lhs2)*100000+lhs2*rhs2)%MOD;
}

最后贴一个完整的代码:

 

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;

const long long MOD=10000000000LL;

struct Edge
{
	int v,next,w;
}buf[50010];

struct Node
{
	int v,dis;
};

const int INF=1<<30;

bool operator<(Node a,Node b)
{
	return a.dis>b.dis;
}

int head[10010];
int dis[10010];
long long cnt[10010];
bool flag[10010];
int N,M,nE,Q;

void addEdge(int u,int v,int w)
{
	buf[nE].v=v;buf[nE].w=w;buf[nE].next=head[u];head[u]=nE++;
}

long long Mul(long long lhs,long long rhs)
{
	long long lhs2=lhs%100000;
	long long rhs2=rhs%100000;
	return ((lhs/100000*rhs2+rhs/100000*lhs2)*100000+lhs2*rhs2)%MOD;
}

long long add(long long a,long long b)
{
	a+=b;
	if(a>=MOD)
		a-=MOD;
	return a;
}

void Dijkstra(int src)
{
	Node tmp,cur;
	tmp.v=src;tmp.dis=0;
	for(int i=0;i<=N;i++)
		dis[i]=INF;
	dis[src]=0;

	memset(flag,false,sizeof(flag));
	memset(cnt,0,sizeof(cnt));
	priority_queue<Node> que;
	cnt[src]=1;
	que.push(tmp);
	while(!que.empty())
	{
		cur=que.top();que.pop();
		int u=cur.v,d=cur.dis;
		if(flag[u])continue;
		flag[u]=true;
		for(int i=head[u];i!=-1;i=buf[i].next)
		{
			int v=buf[i].v,w=buf[i].w;
			if(d+w<dis[v])
			{
				dis[v]=d+w;
				cnt[v]=cnt[u];
				tmp.v=v;tmp.dis=dis[v];
				que.push(tmp);
			}
			else if(d+w==dis[v])
			{	cnt[v]+=cnt[u];
				cnt[v]%=MOD;
			}
		}
	}
}

long long rec[10010];

long long Gao(int s)
{
	long long c=0;
	if(rec[s]!=-1)return rec[s];
	for(int i=head[s];i!=-1;i=buf[i].next)
	{
		int v=buf[i].v;
		if(dis[s]+buf[i].w==dis[v]&&s!=v)
		{
			c++;
			if(rec[v]==-1)
				rec[v]=Gao(v);
			c=add(c,rec[v]);
		}
	}
	return c%MOD;
}

int main()
{
	//freopen("data","r",stdin);
	//freopen("mout","w",stdout);
	while(scanf("%d%d%d",&N,&M,&Q)!=EOF)
	{
		memset(head,0xff,sizeof(head));
		memset(rec,0xff,sizeof(rec));
		nE=0;
		while(M--)
		{
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			addEdge(u,v,w);
		}
		Dijkstra(0);
		int q;
		while(Q--)
		{
			scanf("%d",&q);
			if(cnt[q]<0)while(1);
			long long ans=Mul(cnt[q],Gao(q)+1);
			ans=ans%MOD;
			printf("%010lld\n",ans);
		}
		//putchar(10);
	}
	return 0;
}

POJ 3592 Instantaneous Transference

起初从boat大牛那里听说了这题,挺有意思的,就做了做。

题意:一辆坦克从N*M矩阵的左上角出发,每次往右或往 下走一格,每格可以是'#'(表示不可以走),'*'表示传送门,或者是数字,表示在该格可以获得的值(只能取一次),传送门可以传送到指定位置,你可以选择被传送或者走相邻的格,问坦克可以获得的值的和最大为多少。

解析:第一次看应该是个DP,(如果无传送门且可以走K轮,可以用网络流),但是矩阵中有传送门的存在,有可能形成环,所以要转化成DAG(有向无环图),可以以格子的相邻关系建边,然后求SCC,缩点再构图,边权为指向的格子的值,后求最长路(可以搜索,可以DP)。

WA了好几次,传送门有可能传到'#'的格子,格子是'#'的时候也要向相邻格子建边,题目求的是坦克可以获得的最大值,而不是到右下角的值(上回做了道类似的网络流,惯性导致想当然了)。

最后贴个代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;

struct Edge
{
	int v;
	int next;
}buf[50000];

int head[2000],head2[2000],nE;
bool instack[2200];
int dfn[2020],low[2020],idx,Bcnt;
int stack[2020],top;
int Belong[2020];
int ores[2200];
int Bores[2200];
int N,M;

void addEdge(int u,int v)
{
	buf[nE].v=v;buf[nE].next=head[u];head[u]=nE++;
}

void addEdge2(int u,int v)
{
	buf[nE].v=v;buf[nE].next=head2[u];head2[u]=nE++;
}


void Tarjan(int cur)
{
	dfn[cur]=low[cur]=++idx;
	stack[top++]=cur;
	instack[cur]=true;

	for(int i=head[cur];i!=-1;i=buf[i].next)
	{
		int v=buf[i].v;
		if(!dfn[v])
		{
			Tarjan(v);
			if(low[cur]>low[v])
				low[cur]=low[v];
		}
		else if(instack[v]&&low[cur]>dfn[v])
		{
			low[cur]=dfn[v];
		}
	}
	if(dfn[cur]==low[cur])
	{
		Bcnt++;
		Bores[Bcnt]=0;
		int j;
		do
		{
			j=stack[--top];
			instack[j]=false;
			Belong[j]=Bcnt;
			Bores[Bcnt]+=ores[j];
		}while(j!=cur);
	}
}

void Rebuld()
{
	memset(head2,0xff,sizeof(head2));
	for(int i=0;i<N*M;i++)
		for(int j=head[i];j!=-1;j=buf[j].next)
		{
			int v=buf[j].v;
			if(Belong[i]!=Belong[v])
				addEdge2(Belong[i],Belong[v]);
		}
}

void go()
{
	memset(dfn,0,sizeof(dfn));
	memset(instack,false,sizeof(instack));
	idx=Bcnt=top=0;
	for(int i=0;i<N*M;i++)
		if(!dfn[i])
			Tarjan(i);
	Rebuld();
	int dis[2020];
	memset(dis,0,sizeof(dis));
	dis[Belong[0]]=Bores[Belong[0]];
	queue<int> que;
	que.push(Belong[0]);
	bool inq[2020];
	memset(inq,false,sizeof(inq));
	inq[Belong[0]]=true;
	while(!que.empty())
	{
		int cur=que.front();que.pop();
		inq[cur]=false;
		for(int i=head2[cur];i!=-1;i=buf[i].next)
		{
			int v=buf[i].v;
			if(dis[v]<dis[cur]+Bores[v])
			{
				dis[v]=dis[cur]+Bores[v];
				if(!inq[v])
				{
					inq[v]=true;
					que.push(v);
				}
			}
		}
	}
	int ans=-1;
	for(int i=1;i<=Bcnt;i++)
	   if(dis[i]>ans)
	       ans=dis[i];
	printf("%d\n",ans);
}

char map[45][45];

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(head,0xff,sizeof(head));
		nE=0;
		scanf("%d%d",&N,&M);
		for(int i=0;i<N;i++)
			scanf("%s",map[i]);
		memset(ores,0,sizeof(ores));
		for(int i=0,r,c;i<N;i++)
			for(int j=0;j<M;j++)
				if(map[i][j]!='#')
				{	
					if(i+1<N&&map[i+1][j]!='#')addEdge(i*M+j,(i+1)*M+j);
					if(j+1<M&&map[i][j+1]!='#')addEdge(i*M+j,i*M+j+1);
					ores[i*M+j]=map[i][j]-'0';
					if(map[i][j]=='*')
				    {
                       ores[i*M+j]=0;
                       scanf("%d%d",&r,&c);
                       if(map[r][c]!='#')
                            addEdge(i*M+j,r*M+c);
				    }
				}
		go();
	}
	return 0;
}

0MS

差分约束专题

先摘抄一段:

 

  如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。

  求解差分约束系统,可以转化成图论的单源最短路径问题。
  观察xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]。因此,以每个变量xi为结点,对于约束条件xj-xi<=bk,连接一条边(i,j),边权为bk。我们再增加一个源点s,s与所有定点相连,边权均为0。对这个图,以s为源点运行Bellman-ford算法(或SPFA算法),最终{d[ i]}即为一组可行解。
个人理解,就是根据题目给出的限定关系,得到一组方程组, 统一方程不等号(必须是>= 或者是<=)的方向。根据三角不等式构建出边来,利用spfa求解。求最小值用最长路,求最大值用最短路。
 
poj 1275 Cashier Employment 难度***
题意:一家24小时营业的超市要雇用职员,每个时段需要的职员数量为R(i);每个员工会不间断的工作8小时,现有N个求职者,已知求职者的工作开始时间,问至少要雇用多少职员才能满足超市需求。
解析:黑书上有这道题目。我们设s[i]表示0~i时刻雇用的出纳员总数,r[i]表示每小时要的出纳员数目,t[i]表示每小时申请者的数目。因为由s的定义可知:0<=s[i]-s[i-1]<=t[i]。设s[-1]=0,s[23]-s[-1]>=需要的人数;由于有每小时最低人数的限制,所以s[i]-s[i-8]>=r[i]。综上可有:
s[i]-s[i-1]>=0
s[i-1]-s[i]>=-t[i]
s[23]-s[-1]>=sum
s[ i ]-s[ j ]=r[i] (i>j ,i=(j+8-1)%24+1)
s[ i ]-s[ j ]=r[i]-sum (i<j, i=(j+8-1)%24+1)
二分需要的人数sum,根据以上条件构边,求最长路,判断是否存在正环,是否存在解。
 
poj 1201Intervals 难度***
题意:给定N个区间以及每个区间的Ci,求一个大小最小的整数集合,使该集合落在每个区间上的元素个数大于等于区间的Ci,输出该集合大小。
解析:我们用d[i]来表示集合中从开头到位置i的元素个数。则由d[i]的定义有:0<=d[i]-d[i-1]<=1,d[vi+1]>=d[ui]+Ci;根据以上关系建边求最长路即可。
 
poj 1716 Integer Intervals 难度**
题意:跟1201一样,是1201的简化版,要求集合落在每个区间上的元素个数大于等于2.
解析:把1201的边的w改为2即可。。。。。
 
poj 1364 King 难度**
题意:一个可怜的王二代继承王位了,他只会比较一段连续数字的和与某数字的大小,上位不久就有一批大臣想推翻他,王二代必须证明自己,于是他指出一段连续数字里面某一区间的和大于或小于某个数,你是王二代的谋臣,请为他鉴定是否存在这样的一段连续数字。
解析:理解了题意就很好办了,设su[i]表示第0个到第i这几个数字的和,则如果a~b这段区间的和为sum[a]-sum[b-1]。如果a~b的和大于c则sum[a]-sum[b-1]>c由于差分约束里的三角不等式用的是>=或<=且c恰好是整数,所以有sum[a]-sum[b-1]>=c+1小于的情况也同理。建好边后求负环即可。
代码:
	#include<iostream>
#include <cstdio>
using namespace std;

struct Edge
{
	int u,v,w;
}e[101];

int sum[101];
int M,N;

bool BellmanFord()
{
	for(int i=0;i<=N;i++)
		sum[i]=0;
	for(int k=0;k<N;k++)
		for(int i=0;i<M;i++)
			if(sum[e[i].u]+e[i].w<sum[e[i].v])
				sum[e[i].v]=sum[e[i].u]+e[i].w;
	for(int i=0;i<M;i++)
		if(sum[e[i].u]+e[i].w<sum[e[i].v])
			return false;
	return true;
}

int main()
{
	while(scanf("%d",&N),N)
	{
		scanf("%d",&M);
		char op[3];
		int si,ni,ki;
		for(int i=0;i<M;i++)
		{
			scanf("%d%d%s%d",&si,∋,op,&ki);
			if(op[0]=='g')
			{
				e[i].u=si+ni;
				e[i].v=si-1;
				e[i].w=-ki-1;
			}
			else
			{
				e[i].u=si-1;
				e[i].v=si+ni;
				e[i].w=ki-1;
			}
		}
		if(!BellmanFord())
			printf("successful conspiracy\n");
		else
			printf("lamentable kingdom\n");
	}
	return 0;
}

 

poj 3169 Layout难度*

题意:农夫FJ的牛排成一行等着吃饭,有些牛相互讨厌,他们之间距离的至少大于di,有些牛相互暧昧,他们之间的距离最多小于li问满足条件的队伍的最长距离。

解析:应该是比较水的一个题了(第一次做多校一个类似的题时居然没看出来)。求最大值,用最短路。如果A与B相互讨厌,他们之间的距离应大于di,则有dis[B]-dis[A]>=di,建一条<B,A,-di>的边。暧昧时同理。spfa求最短路时出现负环说明无解,dis[N]=INF则说明牛的位置可以是任意的(因为没有限制条件传递到N,所以N的值是初始的值,即N的位置不受限制),否则输出dis[N]。(其实应该添加dis[i]>=dis[i-1]的,但没加也过了)

 

poj 2983 Is the Information Reliable?难度**

题意:跟上面一题是一样的,这题有两种输入:P A B X表示A到B的距离正好等于X,V A B表示A的距离至少比B大1。问满足条件的解是否存在。

解析:对于P A B X,即dis[A]-dis[B]=X可以转化为:X<=dis[A]-dis[B]<=X ,据此构出两条边。对于V A B 即dis[A]-dis[B]>=1,再构造出边。这题要小心了,很多人用BellmanFord过用SPFA Wa了,要注意有些点是孤立点,或者一些集合构成孤立的集合,所以我们要建一个超级源点,连到所有的点,边的w=0 。最后判负环即可。

 

zoj 1455 Schedule Problem难度***

题意:一个工程分为N个部分,给出每个部分持续时间,以及几对部分之间的关系:FAS, FAF, SAF and SAS.输出每个部分的起始时间。

解析:约束关系比较明朗,以t[i]表示部分i什么时候开始d[i]表示部分i的持续时间,则有:

FAS:  t[a]>=t[b]+d[a]

FAF: t[a]>=t[b]+d[b]-d[a]

SAS: t[a]>=t[b]

SAF: t[a]>=t[b]+d[b]

求最长路,设一个超级源点到所有点的权值为0.最后输出t[i]即可。

图的割点与割边学习笔记

 

割点

割点:如果在图G中删去一个结点u后,图G的连通分枝数增加,即W(G-u)>W(G),则称结点u为G的割点,又称关节点。
     直观地说,就是删除了连通图的某点后,图不在连通,而是分为几个连通分量。

性质:(1)考虑根节点Root,如果Root有数量多于1的子结点时,Root是割点。

      (2)考虑非根结点u,当且仅当u的某个儿子及儿子的子孙均没有指向u的祖先的后向边时,u是割点。(LOW[v]>=DFN[u],v是u的孩子)

代码:

 

void DFS(int cur,int par)
 {
     dfn[cur]=low[cur]=++Index;
     
     int size=adj[cur].size();
     int cnt=0;
     for(int i=0;i<size;i++)
     {    
         int v=adj[cur][i];
         if(!dfn[v])
         {
             cnt++;
             DFS(v,cur);
             if(low[cur]>low[v])
                 low[cur]=low[v];
             if((cur==root&&cnt==2)||(cur!=root&&low[v]>=dfn[cur]))
                 flag[cur]=true;
         }
         else if(v!=par&&low[cur]>dfn[v])
             low[cur]=dfn[v];
     }
 }
 

 

割边

割边:如果在图G中删去一条边e后,图G的连通分支数增加,即W(G-e)>W(G),则称边u为G的桥,又称割边或关节边。

 性质: 对于一条边<u,v>,v是u的孩子如果儿子及儿子的子孙均没有指向u的祖先的后向边时,<u,v>是割边。(LOW[v]>DFN[u])

代码:

 

void CutEdge(int cur,int par)
 {    dfn[cur]=low[cur]=++Index;
     
      for(int i=head[cur];i;i=buf[i].next)
     {
         int v=buf[i].v;
         if(v==par)continue;
         if(!dfn[v])
         {
             CutEdge(v,cur);
             if(low[cur]>low[v])
                 low[cur]=low[v];
             if(low[v]>dfn[cur])
             {    
                     ans[nAns++]=buf[i].id;
             }
         }
         else if(low[cur]>dfn[v])
             low[cur]=dfn[v];
     }
 }

 

相关习题:

POJ 1144 Network 赤果果的求割点的题。

相关链接:
Beyond the Void的讲解,很精彩

推荐阅读:

lrj的黑书P285

网络流题目记录

同之前的线段树题目记录一样,持续更新中,在此膜拜一下小HH

hdu 3081 Marriage Match II

n男n女过家家,女生可以挑没有跟她吵过架的男生匹配,也可与没有跟她朋友吵过架的男生匹配(可以认为是她喜欢的男生)。每轮游戏,每个女生选个她没选过的男生,问游戏最多能进行几轮?

构图:先用并查集处理女生友情的传递。每个女向她喜欢的所有男生以及她朋友喜欢的男生连一条容量为1的边。二分最大能进行的轮数ans,源点向每个女生连一条容量为ans的边,每个男生向汇点连一条容量为ans的边。判断最大流是否等于ans*N;

 

hdu 3277 Marriage Match III

同上,每个女生可以跟最多K个她不喜欢的男生匹配。

构图:每个女生拆为两个点Gi1,Gi2。Gi1连一条容量为K的边到Gi2。每个Gi2连一条容量为1的边到女生i不喜欢的男生,源点连容量为ans的边到每个Gi1。其余同上

一开始用Dinic,无限TLE~~~ T_T 。万般无奈下,只好上Qinz大牛的博客膜拜了ISAP的模板,再结合网上各路神牛的论文,终于得出了一份自己的ISAP模板~,提交果断AC~

 

hdu 3416 Marriage Match IV

有向图,求起点到终点的最短路的条数。

构图:求最短路,起点到所有点的最短路,所有边反向,求终点到所有点的最短路,对于边如果起点到u的距离加终点到v的距离加这条边的权值等于最短路长,那么在网络流的图中加一条u指向v,权值为1的边。求起点到终点的最大流。

poj 3281 Dining 

N头牛,D种饮料,F种食物,每天牛吃一种食物一种饮料,食物和饮料都只有一份。问最大满足多少头牛。

构图:由于每头牛只需一份饮料和食物,所以每头牛要拆为两点,连容量为1的边。起点到所有食物连容量为1的边,饮料到汇点连容量为1的边。牛再和食物,饮料连。Dinic 0MS毫无压力。

poj 2135 Farm Tour 

求起点到终点再回起点的最短路径。不能走重复的路。

构图:第一次写费用流。用的是朴素的spfa。费用是距离。源点到1连一条容量为2的边。终点到汇点连容量为2的边。每条路径的容量都为1.直接水过。

poj 2711 Leapin' Lizards 

图上有些柱子,开始时,一些蜥蜴在柱子上,当蜥蜴跳离柱子时,柱子高度降1,蜥蜴不能跳到高度为0的柱子上,蜥蜴跳跃的距离为D,求有多少蜥蜴能够跳出来。

构图:把每个有高度柱子拆为两点,ai,bi,ai->bi容量为柱子高度。源点接到有蜥蜴的柱子ai上,能一步跳出去的柱子的bi接到汇点,容量INF,距离为D的任意两格子都连容量INF的边。求解最大流。求曼哈顿距离的时候出了点小问题,无限WA。注意输出,单复数是不同的。

poj 2516 Minimum Cost

有N个客户订单,M座仓库。给出订单内容和仓库库存,以及仓库到客户的费用。求满足所有客户需求的最小费用。

构图:原生态最小费用流。每种商品都是独立的,那么我们可以针对每种商品计算最小费用。从源点连容量为仓库i中k种商品库存费用为0的边到仓库i,仓库i接容量为无限,费用为到客户j的费用。如果最大流等于k的需求总量那么k就满足了。可以记录每种商品需求总量和库存总量。供不应求则输出-1 。

poj 3680 Intervals

已知N条线段的端点和线段的权值,选一些线段使权值最大,坐标轴上一点最多被覆盖K次。

构图:费用流。原打算拆点连边。感觉复杂度过不了,膜拜了传说中神一般的构图:先把点离散化,起点到第一点连权值为0,容量为K,i到i+1连权值为0容量为K的边,直到汇点。一条线段的起始点连一条容量为1权值为-w的边到结束点。

 

poj 3498 March of the Penguins

有点类似2711,把柱子改成了冰块。而冰块上有一些企鹅,企鹅们要选一块冰块聚会,聚会点要求所有企鹅都能到达。给出企鹅的跳跃距离。找出所有的聚会点。

构图:基本同2711,这回要枚举点作为汇点。只要最大流等于除这冰块上的所有企鹅个数即可作为集结点,注意冰块是0起始的(调试到半夜一直出不了样例,原来。。。。)。

 

poj 3469 Dual Core CPU

网络流经典题目。最小割。一块CPU使用A模式要耗费Ai使用B模式耗费Bi,不同CPU交换数据耗费Wij,求a到b模式工作的最小耗费。

构图:题意比较囧,总之,源点连到所有CPU权值为使用Ai,CPU到汇点的权值为Bi。不同的CPU连Wij的边。求最大流,根据最大流最小割得解。

 

poj 1149 PIGS

农夫有M猪圈,有一天他要卖猪,但他没有猪圈钥匙而顾客有(好囧)。顾客来了以后打开他能打开的所有猪圈。买走一定数量的猪,然后关门。农夫可以在关门前把剩余的猪转移到别的猪圈。问农夫最多能卖多少猪。

构图:直接以猪圈和顾客为点,数量为边,TLE了。这题构图好难,这篇文章解释得很Nice~膜拜!

 
poj 2391 Ombrophobic Bovines
F块田上有些牛,以及牛棚,要下雨了,牛要返回牛棚。牛棚的容量有限,有些无向路连接这些牛棚。给出牛棚容量,牛数,每条路径时间。求最晚什么时候牛必须全回牛棚。
构图:先拿FLOYD计算一下各个牛棚间的距离,为了避免网络流过程中出现传递,每个牛棚拆为两点,入点和出点。源点到牛棚入点连容量为牛数量的边,牛棚出点连牛棚容量的边到汇点。二分答案,如果A到B的距离小于等于mid,那么AB之间可以连边。判断最大流是否等于牛总数。
poj 3422 Kaka's Matrix Travels
有一个N*N的矩阵,每次从矩阵左上角出发,到达右下角,且只能往下或往右走。每走过一个格子,sum加上该元素(只加一次),求走了K次以后所能得到的和最大是多少。
构图:一开始以为是动态规划,但动态规划在K=1的时候有用,K>1我就不会了,好在N不大,可以用费用流来解,每个元素拆为两点,AB,Ai向Bi连容量为1,费用为该元素值的边,再连一条容量为INF,费用为0的边(保证这个格子走了一次以后还可以走第二次,且费用不算)。每个格子的B再向下边和右边的格子的A连一条容量INF,费用0 的边。最后求最大费用流。