1. 程式人生 > >網絡流合集:bzoj1433,1934,1854 題解

網絡流合集:bzoj1433,1934,1854 題解

struct spa tail set esc urn 這也 space 宿舍

轉載請註明: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

Day2


【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

Day1


【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;
}

網絡流合集:bzoj1433,1934,1854 題解