原题地址:点击打开链接
题意:一个青蛙要从河的左边跳到右边,河中有许多的石头排在一条直线上,有大的石头可以踩无数次,而小的石头只能踩一次。
问你青蛙最少一次能跳多远才能从河的左边跳到右边,在从右边跳回左边。
思路:可用最大流求解,该题的难点在与建图,把每个石头看成是一个顶点,由于顶点上也有流量限制,所以要进行拆点,如果是
大石头就自身与自身建一条流量为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
*/