1079. 【GDKOI2007】纳克萨玛斯
Description#
“英雄们,天灾亡灵军团已经入侵到暴风城的周围村庄了,为了艾泽拉世界的未来让我们携手对抗敌人吧!”先知 eternized 召集了联盟和部落的领袖们,准备组建一只讨伐军征战“纳克萨玛斯”——天灾亡灵的大本营。
三天过后,联盟首领 bug 和部落首领 spacesheep 分别带来了各自阵营里面最精英的部队过来。这批勇士一共有 N 人,属于各种族和各种职业。他们不但有能沟通大自然的牛头人德鲁依,也有圣光保佑的圣骑士矮人,甚至还有能连通阴阳界的精灵术士。
“好吧,现在我们来挑选勇士吧。”先知首先挑选中一些勇士,然后将他们分为一个个单独的小队,其中每个小队包括 M 种角色,比如说队长(leader),攻击(Attack)、治疗(Cure)、防御(Tank)等,而且队中每种角色有且仅有一个。一个人只能充当一个角色。比如德鲁依,受到自然的恩惠,使他能成为一个合格的医疗师和防御者,而暗影牧师则能提供足够的攻击力和防御力。另外,先知为了平衡各方阵营利益,联盟和部落各被选上的人数相差不能超过 D 人。
先知当然希望能组织起来的小队越多越好。聪明的你,你能给出一个最优方案么?
Solution#
先不管 这个限制,那么我们可以建源点和汇点,分别连向人和职业,同时枚举答案 (当然也可以二分),其中职业向汇点的边的流量为 ,源点向人的流量为 1,人向职业的流量为 ,每次重新建图跑最大流,直到最大流 ,那么 就是答案。
现在考虑加上限制 。设联盟人数为 ,部落人数为 ,假定 。那么有 ,。变一下可以得到 。
因此在建图时,改一下源点到人的连接方式,新增两个节点,一个连联盟,一个连部落,到人的流量为 1。源点流向这两个点的流量都为 。这样就可以满足限制。
但这样的话边的数量可能会达到 ,网络流肯定会超时,考虑优化建图方法。
发现即使所有状态都存在的话,每个人的类型最多只有 种,远远小于 。并且每个状态相同的人之间是没有区别的,因此我们可以记录每种状态的数量,将流量从1 改为数量,并且左边不再是人而是状态,可以减少边数,从而降低复杂度。
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)