1079. [GDKOI2007] Naxxramas

Posted by QSDragon on Fri, 21 Jan 2022 09:45:13 +0100

Description

"Heroes, the scourge of the dead has invaded the villages around storm city. For the future of Azera world, let's fight the enemy hand in hand!" The prophet eternized summoned the leaders of the alliance and tribe to form a crusade army to fight Naxxramas, the headquarters of the victims of natural disasters.
Three days later, alliance leader bug and tribal leader spacesheet brought the most elite troops from their respective camps. There are N warriors in this group, belonging to various races and occupations. They not only have Tauren druids who can communicate with nature, but also Paladin dwarves blessed by the holy light, and even elf warlocks who can connect the Yin and Yang.
"Well, now let's choose the warriors." The prophet first selected some warriors and then divided them into separate teams. Each team includes M roles, such as leader, Attack, Cure, Tank, etc., and there is only one role in each team. One person can only play one role. For example, Druid is blessed by nature so that he can become a qualified healer and defender, while the shadow priest can provide enough Attack and defense. In addition, in order to balance the interests of all camps, the difference in the number of elected members between the alliance and the tribe should not exceed D.
The prophet certainly hopes that the more teams can be organized, the better. Smart you, can you give an optimal solution?

Solution

Regardless of the limitation of \ (D \), we can create source points and sink points to connect people and occupations respectively, and enumerate the answers \ (ANS \) (of course, it can also be divided into two points). The flow of occupations to sink points is \ (ANS \), the flow of source points to people is 1, and the flow of people to occupations is \ (\ inf \). Each time we rebuild the map, run the maximum flow until the maximum flow \ (flow < ans \ times m \), So \ (ans-1 \) is the answer.

Now consider adding a limit \ (D \). Let the number of allies be \ (x \) and the number of tribes be \ (Y \), assuming \ (x > y \). Then there are \ (x+y=ans\times m \), \ (x-y\le D \). Change to get \ (x\le \lfloor\frac{D+ans\times m}{2}\rfloor \).

Therefore, when building a map, change the connection mode from the source point to people, and add two nodes, one company alliance and one company tribe. The traffic to people is 1. The flow from the source point to these two points is \ (\ lfloor\frac{D+ans\times m}{2}\rfloor \). In this way, the limit can be met.

However, in this case, the number of edges may reach \ (10 ^ 6 \), and the network flow will definitely timeout. Consider optimizing the mapping method.

It is found that even if all states exist, each person has only \ (2^m \) types at most, which is far less than \ (n \). And there is no difference between people in the same state, so we can record the number of each state, change the traffic from 1 to the number, and the left is no longer a person but a state, which can reduce the number of sides and reduce the complexity.

Code

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 3000
#define inf 2147483647
using namespace std;
struct node
{
    int to,next,flow;
}a[N*20];
int n,m,d,ans,tot,S,T,SS,TT,mx,answer,t[2000][2],head[N],cur[N],deep[N];
char ch[20];
void add(int x,int y,int z)
{
    a[++tot].to=y;a[tot].flow=z;a[tot].next=head[x];head[x]=tot;
    a[++tot].to=x;a[tot].flow=0;a[tot].next=head[y];head[y]=tot;
}
bool bfs()
{
    memset(deep,0,sizeof(deep));
    queue<int> q;
    deep[S]=1;q.push(S);
    while (!q.empty())
    {
        int x=q.front();q.pop();
        for (int i=head[x];i!=-1;i=a[i].next)
        {
            int y=a[i].to;
            if (!deep[y]&&a[i].flow)
            {
                deep[y]=deep[x]+1;
                q.push(y);
                if (y==T) return true;
            }
        }
    }
    return false;
}
int dfs(int x,int flow)
{
    if (x==T) return flow;
    int res=0;
    for (int &i=cur[x];i!=-1;i=a[i].next)
    {
        int y=a[i].to;
        if (a[i].flow&&deep[y]==deep[x]+1)
        {
            int fl=dfs(y,min(flow,a[i].flow));
            if (!fl) continue;
            a[i].flow-=fl;a[i^1].flow+=fl;
            res+=fl;flow-=fl;
            if (!flow) break;
        }
    }
    return res;
}
int main()
{
    scanf("%d%d%d",&n,&m,&d);
    for (int i=1;i<=n;++i)
    {
        scanf("%s",ch+1);
        int typ=ch[m+1]-'0',sum=0;
        for (int j=1;j<=m;++j)
            sum+=(ch[j]-'0')*(1<<(j-1));
        ++t[sum][typ];
    }
    mx=(1<<m);
    S=2*mx+m;T=S+1;SS=T+1;TT=SS+1;
    for (ans=1;ans<=n;++ans)
    {
        memset(head,-1,sizeof(head));
        tot=-1;
        add(S,SS,(d+ans*m)/2);add(S,TT,(d+ans*m)/2);
        for (int i=0;i<mx;++i)
            add(SS,i,t[i][0]),add(TT,i+mx,t[i][1]);
        for (int i=0;i<=mx;++i)
            for (int j=0;j<m;++j)
                if (((1<<j)&i)) add(i,j+2*mx,inf),add(i+mx,j+2*mx,inf);
        for (int i=1;i<=m;++i)
            add(i+2*mx-1,T,ans);
        int res=0;
        while (bfs())
        {
            for (int i=1;i<=TT;++i)
                cur[i]=head[i];
            res+=dfs(S,inf);
        }
        if (res<ans*m&&d!=0)
        {
            printf("%d\n",ans-1);
            return 0;
        }
        else if (res>=ans*m) answer=ans;
    }
    printf("%d\n",answer);
    return 0;
}

Topics: network-flows