LightOJ - 1156-Jumping Frogs (二分+最大流+拆点)

原题地址:点击打开链接

题意:一个青蛙要从河的左边跳到右边,河中有许多的石头排在一条直线上,有大的石头可以踩无数次,而小的石头只能踩一次。

问你青蛙最少一次能跳多远才能从河的左边跳到右边,在从右边跳回左边。

思路:可用最大流求解,该题的难点在与建图,把每个石头看成是一个顶点,由于顶点上也有流量限制,所以要进行拆点,如果是

大石头就自身与自身建一条流量为2的点(因为只需要往返一次所以流量设为2即可),如果是小石头就自身与自身建一条流量为1的点,意思只能

经过一次,然后在用二分枚举出青蛙能跳的距离d,在判断每两个顶点之间的距离,如果小于d,则证明青蛙可以从这个石头跳向另一个石头,然后建

一条流量为2的边,别忘了源点与汇点之间也要建一条边.之前一直wa,半天没有找到原因,改了输入数据的方式之后突然就对了,,略坑。。

#include<stdio.h>
#include<string.h>
#include<vector>
#define INF 1<<31-1
#define min(x,y)(x<y?x:y)
using namespace std;
struct Edge
{
	int to;
	int cap;
	int rev;
};
struct Point
{
	int dis;
	int state;
}p[210];
vector<Edge>g[210];
int used[210]; 
int left,right;
void add_edge(int from,int to,int cap)
{
	g[from].push_back((Edge){to,cap,g[to].size()});
	g[to].push_back((Edge){from,0,g[from].size()-1});
}
int dfs(int u,int t,int f)
{
	if(u==t)
	  return f;
	  used[u]=1;
	for(int i=0;i<g[u].size();i++)
	{
		Edge &e=g[u][i];
		int v=e.to;
		if(!used[v]&&e.cap>0)
		{
			int d=dfs(v,t,min(e.cap,f));
			if(d>0)
			{
				e.cap-=d;
				g[e.to][e.rev].cap+=d;
				return d;
			}
		}
	}
	return 0;
}
bool max_flow(int s,int t)
{
	int res=0;
	while(1)
	{
		memset(used,0,sizeof(used));
		int d=dfs(s,t,INF);
		if(d<=0)
			break;
		res+=d;
		if(res>=2)
			break;
	}
	if(res>=2)                   //如果能走2次就没必要在继续判断了 
		return true;
	return false;
}
bool judge(Point p1,Point p2,int d)   //判断两个石头的距离是否小于能跳的最远距离 
{
	int res=p1.dis-p2.dis;
	if(res<0) res=-res;
	if(res<=d)
		return true;
	return false;
}
void makeNew(int s,int t,int n,int d)
{
	int i,j;
	for(i=s;i<=t;i++)
		g[i].clear();
	for(i=1;i<=n;i++)            //拆点自身与自身建一条边,如果是大石头流量就2,否则1 
		{
			if(p[i].state==1)
			{
				add_edge(i,i+n,2);
			}
			else
			{
				add_edge(i,i+n,1);
			}
		}
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
			{
				if(i!=j&&judge(p[i],p[j],d))
					add_edge(i+n,j,2);
			}
		for(i=1;i<=n;i++)
		{
			if(judge(p[s],p[i],d))
			add_edge(s,i,2);
			if(judge(p[t],p[i],d))
			add_edge(i+n,t,2);
		}
		if(judge(p[s],p[t],d))
			add_edge(s,t,2);
}
int solve(int s,int t,int n)
{
	while(left<right-1)            //用二分枚举每次能跳的最大的距离 
	{
		int mid=(left+right)/2; 
		 makeNew(s,t,n,mid);
		 if(max_flow(s,t))
		 	right=mid;
		else 
			left=mid;
	}
	return right;
}
int main()
{
	int c,s,t,n,d,i,k=0,x;
	char ch;
	scanf("%d",&c);
	while(c--)
	{
		scanf("%d%d",&n,&d);
		right=INF;
		left=0;
		s=0; t=n*2+1;
		p[s].dis=0;
		p[t].dis=d;
		for(i=1;i<=n;i++)
		{
			scanf(" %c-%d",&ch,&x);
			if(ch=='B')
				p[i].state=1;
			else
				p[i].state=0;
			p[i].dis=x;
		}
		int res=solve(s,t,n);
		printf("Case %d: %d\n",++k,res);
	}
	return 0;
}
/*


3
1 10
B-5
1 10
S-5
2 10
B-3 S-6
2 10

*/ 


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值