题目描述
Bob有(2^n)字节的内存,编号为([0,2^n-1))。他想对每个字节的内存分别分配一个值。对于编号为(i)的内存,如果它被分配了一个值(j(0leq j<2^m)),那么该字节产生的基本欢乐值为(w_{i,j})。
更欢乐的是,每一个字节还有一个临界值(c_i)。对于两个不同的字节,编号分别为(a,b(a<b)),如果以下两个条件同时成立,那么就会产生额外欢乐值(u_a ext{^}u_b)。
1.在二进制下,(a)和(b)有且仅有一位不同(例如(4)和(5),(4)的二进制为(100),(5)的二进制为(101),有且仅有第(3)位不同)。
2.在(a)字节分配的值不少于(a)字节的临界值(c_a),或者在(b)字节分配的值不少于(b)字节的临界值(c_b)。
一种分配方法的总欢乐值是每个字节的基本欢乐值的总和加上额外欢乐值。
Bob想找到一种最佳分配方法,使得总欢乐值最大。如果有多组解,请任意输出一种。
(1leq n,mleq 8,0leq c_i<2^m,0leq u_i<1024,-1024leq w_{i,j}<1024)
题解
对于一个字节的内存(i),分配的值只可能是两个,(j<c_i)和(jgeq c_i)的使(w_{i,j})最大的(j),分别是(v1_i,v2_i)。
因为对于每一组满足条件的(a,b),只有两个位置都小于(c_a,c_b)时才没有收益。可以看出这是一个最小割的模型,割就是要放弃的收益。
但是还有一个问题,两个点都割掉左边的边才有额外代价,这就会出现问题。我们观察一下一般的棋盘是怎么解决这个问题的:黑白染色。那么这个问题能不能用类似的方法做呢?答案是可以。因为两个不同的点连边的条件是二进制位相差(1),这样连边是不会出现奇环的,所以我们可以把二进制中(1)的个数为奇数的点反过来连边
所以连边方案就变成了:
1.如果(i)的二进制中(1)的个数为奇数,就连边((S,i,v1_i)),割掉这条边代表选择(v2_i)。同样的,连边((i,T,v2_i))。
2.如果(i)的二进制中(1)的个数为偶数,就连边((S,i,v2_i))和((i,T,v1_i))。
3.如果(a,b)满足(a)的二进制中(1)的个数是奇数,(b)的二进制中(1)的个数是偶数,就连边((a,b,u_a ext{^}u_b))。这条边只有在((S,a))和((b,T))都被选择,即选择(v1_a)和(v1_b)的情况下才会被选中。
然后跑网络流,用边权和减掉流量就是答案。
那么会有几个问题。
1.怎么算每个点选那边?从原点开始BFS,遍历到的点都选另一边(即((i,T))对应的方案),没遍历到的点选这一边(((S,i)))。
2.怎么算每个点分配什么值?从大到小枚举,第一个满足条件的就是答案。因为(w_{i,j})相同的情况下选(j)比较大的不会比选(j)比较小的劣。
3.边权可能是负数。因为所有边权都(geq-1024),可以把所有边权都加上一个(geq1024)的数。
时间复杂度:(O(???))
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
struct list
{
int v[100010];
int c[100010];
int t[100010];
int h[100010];
int n;
list()
{
memset(h,0,sizeof h);
n=0;
}
void add(int x,int y,int z)
{
n++;
v[n]=y;
c[n]=z;
t[n]=h[x];
h[x]=n;
}
};
list l;
void add(int x,int y,int z)
{
l.add(x,y,z);
l.add(y,x,0);
}
int op(int x)
{
return ((x-1)^1)+1;
}
int d[1<<9];
int S,T;
int bfs()
{
memset(d,-1,sizeof d);
d[S]=0;
queue<int> q;
q.push(S);
while(!q.empty())
{
int x=q.front();
q.pop();
int i;
for(i=l.h[x];i;i=l.t[i])
if(l.c[i]&&d[l.v[i]]==-1)
{
d[l.v[i]]=d[x]+1;
if(l.v[i]==T)
return 1;
q.push(l.v[i]);
}
}
return 0;
}
int dfs(int x,int flow)
{
if(x==T)
return flow;
int c,s=0;
int i;
for(i=l.h[x];i;i=l.t[i])
if(l.c[i]&&d[l.v[i]]==d[x]+1)
{
c=dfs(l.v[i],min(flow,l.c[i]));
s+=c;
flow-=c;
l.c[i]-=c;
l.c[op(i)]+=c;
if(!flow)
break;
}
if(flow)
d[x]=-1;
return s;
}
int w[1<<9][1<<9];
int u[1<<9];
int c[1<<9];
int v1[1<<9];
int v2[1<<9];
int ans[1<<9];
int b[1<<9];
int bitcount(int x)
{
int s=0;
while(x)
{
x-=x&-x;
s++;
}
return s;
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
int i,j;
for(i=1;i<=(1<<n);i++)
scanf("%d",&c[i]);
for(i=1;i<=(1<<n);i++)
scanf("%d",&u[i]);
memset(v1,0x80,sizeof v1);
memset(v2,0x80,sizeof v2);
for(i=1;i<=(1<<n);i++)
for(j=0;j<(1<<m);j++)
{
scanf("%d",&w[i][j]);
if(j<c[i])
v1[i]=max(v1[i],w[i][j]);
else
v2[i]=max(v2[i],w[i][j]);
}
for(i=1;i<=(1<<n);i++)
b[i]=bitcount(i-1);
S=(1<<n)+1;
T=(1<<n)+2;
int sum=0;
for(i=1;i<=(1<<n);i++)
if(b[i]&1)
{
if(v1[i]>-2000)
add(S,i,v1[i]+2000);
if(v2[i]>-2000)
add(i,T,v2[i]+2000);
for(j=1;j<=(1<<n);j++)
if(bitcount((i-1)^(j-1))==1)
add(i,j,u[i]^u[j]);
}
else
{
if(v2[i]>-2000)
add(S,i,v2[i]+2000);
if(v1[i]>-2000)
add(i,T,v1[i]+2000);
}
int s=0;
while(bfs())
s+=dfs(S,0x7fffffff);
for(i=1;i<=(1<<n);i++)
if((d[i]==-1)^(b[i]&1))
ans[i]=v1[i];
else
ans[i]=v2[i];
for(i=1;i<=(1<<n);i++)
for(j=(1<<m)-1;j>=0;j--)
if(w[i][j]==ans[i])
{
printf("%d ",j);
break;
}
return 0;
}