网络流合集: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;
}

posted @ 2017-05-07 13:27  wzzkaifa  阅读(140)  评论(0编辑  收藏  举报