网络流合集:bzoj1433,1934,1854 题解

转载请注明:http://blog.csdn.net/jiangshibiao/article/details/23992205          

          

            网络流/二分图大合集

【NO.1*原题】

1433: [ZJOI2009]假期的宿舍

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 972   Solved: 422
[ Submit][ Status]

Description

Input

Output

Sample Input

1
3
1 1 0
0 1 0
0 1 1
1 0 0
1 0 0

Sample Output

ˆ ˆ

HINT

对于30% 的数据满足1 ≤ n ≤ 12。
对于100% 的数据满足1 ≤ n ≤ 50,1 ≤ T ≤ 20。


【NO.1*分析】想了很长时间。igzhou大神非要我用二分图写,但我就是要转化成网络流。首先我们把住校的人(也就是有床的人)的床和汇点T连边。注意这是”床“,而不是人本身。然后把源点S和所有人连一条边。再把互相认识的人中人和床连边。比如1是住校的,2是走读的,2认识1,就把2和1的床连边。注意不是2和1连边。自然,对于住校生,自己和自己的床也要连边。以上所有边的容量都是1。

坑点:输出不用回车。

NO.1*代码】

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 305
#define INF 2000000000
using namespace std;
int map[N][N],f[N],q[N],num[N],x,test,ans,n,m,i,j,cnt;
bool bfs()
{
  memset(f,-1,sizeof(f));
  int h=0,t=1;q[1]=0;f[0]=1;
  while (h<t)
  {
    int now=q[++h];if (now==n) return 1;
    for (int i=0;i<=n;i++)
      if (map[now][i]&&f[i]==-1)
      {
        f[i]=f[now]+1;q[++t]=i;
      }
  }
  return 0;
}
int dinic(int sta,int sum)
{
  if (sta==n) return sum;
  int os=sum;
  for (int i=0;(i<=n)&&os;i++)
    if (map[sta][i]&&f[i]==f[sta]+1)
    {
      int Min=dinic(i,min(map[sta][i],os));
      map[sta][i]-=Min;map[i][sta]+=Min;os-=Min;
    }
  if (os==sum) f[sta]=-1;
  return sum-os;
}
int main()
{
  scanf("%d",&test);
  while (test)
  {
    test--;memset(map,0,sizeof(map));
    scanf("%d",&n);cnt=0;
    for (i=1;i<=n;i++)
    {
      scanf("%d",&num[i]);
      if (num[i]) map[i+n][2*n+1]=1;
    }
    for (i=1;i<=n;i++)
    {
      scanf("%d",&x);
      if (!num[i]||num[i]&&!x) map[0][i]=1,cnt++;
    }
    for (i=1;i<=n;i++)
      for (j=1;j<=n;j++)
      {
        scanf("%d",&x);
        if (x||i==j) map[i][j+n]=1;
      }
    n=2*n+1;ans=0;
    while (bfs()) ans+=dinic(0,INF);
    if (ans==cnt) puts("^_^");else puts("T_T");
  }
  return 0;
}

【NO.2*原题】

1934: [Shoi2007]Vote 善意的投票

Time Limit: 1 Sec   Memory Limit: 64 MB
Submit: 897   Solved: 537
[ Submit][ Status]

Description

幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉。对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神。虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的人数。 我们的问题就是,每位小朋友应该怎样投票,才能使冲突数最小?

Input

第一行只有两个整数n,m,保证有2≤n≤300,1≤m≤n(n-1)/2。其中n代表总人数,m代表好朋友的对数。文件第二行有n个整数,第i个整数代表第i个小朋友的意愿,当它为1时表示同意睡觉,当它为0时表示反对睡觉。接下来文件还有m行,每行有两个整数i,j。表示i,j是一对好朋友,我们保证任何两对i,j不会重复。

Output

只需要输出一个整数,即可能的最小冲突数。

Sample Input

3 3
1 0 0
1 2
1 3
3 2

Sample Output

1

HINT

在第一个例子中,所有小朋友都投赞成票就能得到最优解

Source


【NO.2*分析】我感觉这个的建图有点诡异。把所有1的小朋友和S连,否则和T连,再朋友间互相连。求最大流。

NO.2*代码】

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 302
#define INF 2000000000
using namespace std;
int map[N][N],f[N],q[N],num,x,y,ans,n,m,i;
bool bfs()
{
  memset(f,-1,sizeof(f));
  int h=0,t=1;q[1]=0;f[0]=1;
  while (h<t)
  {
    int now=q[++h];if (now==n) return 1;
    for (int i=0;i<=n;i++)
      if (map[now][i]&&f[i]==-1)
      {
        f[i]=f[now]+1;
        q[++t]=i;
      }
  }
  return 0;
}
int dinic(int sta,int sum)
{
  if (sta==n) return sum;int os=sum;
  for (int i=0;(i<=n)&&os;i++)
    if (map[sta][i]&&f[i]==f[sta]+1)
    {
      int Min=dinic(i,min(map[sta][i],os));
      map[sta][i]-=Min;map[i][sta]+=Min;os-=Min;
    }
  if (os==sum) f[sta]=-1;return sum-os;
}
int main()
{
  scanf("%d%d",&n,&m);
  for (i=1;i<=n;i++)
  {
    scanf("%d",&num);if (num) map[0][i]=1;else map[i][n+1]=1;
  }
  for (i=1;i<=m;i++)
  {
    scanf("%d%d",&x,&y);map[x][y]=map[y][x]=1;
  }
  n++;while (bfs()) ans+=dinic(0,INF);
  printf("%d",ans);
  return 0;
}

【NO.3*原题】

1854: [Scoi2010]游戏

Time Limit: 5 Sec   Memory Limit: 162 MB
Submit: 1954   Solved: 689
[ Submit][ Status]

Description

lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。 游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。 现在lxhgww想知道他最多能连续攻击boss多少次?

Input

输入的第一行是一个整数N,表示lxhgww拥有N种装备 接下来N行,是对这N种装备的描述,每行2个数字,表示第i种装备的2个属性值

Output

输出一行,包括1个数字,表示lxhgww最多能连续攻击的次数。

Sample Input

3
1 2
3 2
4 5

Sample Output

2

HINT

【数据范围】
对于30%的数据,保证N < =1000
对于100%的数据,保证N < =1000000

Source


【NO.3*分析】这个构图是挺经典的。最后我是用二分图A的。其实这也挺好理解。左侧是各种属性,右侧是各种装备。对于每一个装备,连两个属性。这样就能保证选了一个后,对于同一个装备的另一个属性,就不会选了。

NO.3*代码】

#include<cstdio>
#include<cstring>
using namespace std;
struct arr{int go,next;}a[2000005];
int belong[1000005],end[10005];
int visit[1000005];
int x,y,n,i,cnt;
char ch;
void add(int x,int y)
{
  a[++cnt].go=y;a[cnt].next=end[x];end[x]=cnt;
}
inline int Read()
{
  while (ch<'0'||ch>'9') ch=getchar();
  int s=0;while (ch>='0'&&ch<='9') s=s*10+ch-48,ch=getchar();return s;
}
bool find(int k)
{
  for (int i=end[k];i;i=a[i].next)
  {
    int go=a[i].go;
    if (visit[go]==i) continue;
    visit[go]=i;
    if (!belong[go]||find(belong[go]))
    {
      belong[go]=k;
      return true;
    }
  }
  return false;
}
int main()
{
  scanf("%d",&n);ch=' ';
  for (i=1;i<=n;i++)
  {
    x=Read();y=Read();
    add(x,i);add(y,i);
  }
  memset(belong,0,sizeof(belong));
  for (i=1;i<=10000;i++)
    if (!find(i)) break;
  i--;printf("%d",i);/
  return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值