国际大学生程序设计竞赛例题_5.3狙击兵

1,起点:0,终点:n
1到n-1设置n-1的狙击点,给定边和每个点至少需要的狙击兵数量.
求:成功阻击最少需要多少个狙击手?
2,解答:
最大流最小割定理.
这里需要将一个点拆成一条边.
举例:
[img]http://dl.iteye.com/upload/attachment/209813/71cb4738-6809-3530-ada4-ae9bb395ea0e.bmp[/img]
相应的生成图为:

[img]http://dl.iteye.com/upload/attachment/209817/c2cd5ad3-03cd-3f1d-a065-75512c5230d2.bmp[/img]

3,实例代码:

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

const int maxN=105;
const int inf=0xffff;

bool g[maxN][maxN];//关系数组,初始图
int edge[maxN][maxN];//生成图的邻接权矩阵

int n,m;//顶点数和边数
int cnt;//测试数目
int ans;

bool visited[maxN];
int father[maxN];

void Ford_Fulkerson()
{
while(1)
{
//一次大循环,找到一条可能的增广路径
queue <int> q;
memset(visited, 0, sizeof(visited));
memset(father, -1, sizeof(father));
int now;
visited[0] = true;
q.push(0);
while(!q.empty())//广度优先
{
now = q.front();
q.pop();
if(now == n-1) break;
for(int i=0; i<n+n-2; i++) //生成图定点数
{
//每次父亲节点都要更新,权值减为0的边就不算了.
if(edge[now][i] && !visited[i])
{
father[i] = now;
visited[i] = true;
q.push(i);
}
}
}
//可能的增广路不存在了
if(!visited[n-1]) break; //终点标号
int u, min = inf;
for(u=n-1; u; u=father[u])//找出权值最小的边
{
if(edge[father[u]][u] < min)
min = edge[father[u]][u];
}
//减去最小权值
for(u=n-1; u; u=father[u])
{
//前向弧减去
edge[father[u]][u] -= min;
//后向弧加上
//存在圆环,这句话关键
edge[u][father[u]] += min;
}
//当前增广路径增加的流
ans+=min;
}
}

int main()
{
freopen("5.3.in","r",stdin);
cin>>cnt;
int s,e,temp;
while(cnt--)
{
ans=0;
memset(g,0,sizeof(g));
memset(edge,0,sizeof(edge));
cin>>n>>m;
n+=1;
for(int i=1;i<=n-2;i++)
{
int num;//狙击兵个数
cin>>num;
//将点分成一条边
edge[i][i+n-1]=num;
}
for(int i=0;i<m;i++)
{
cin>>s>>e;
if(s>e)
{
temp=s;
s=e;
e=temp;
}
g[s][e]=true;
if(s!=0&&e!=n-1) //不含有起点或终点,设定为双向边
g[e][s]=true;
}

for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(g[i][j])
{
if(i==0) edge[i][j]=inf;
if(i!=0) edge[i+n-1][j]=inf;
}
}
Ford_Fulkerson();
cout<<ans<<endl;
}
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值