题目大意
给定一幅
n
n
n 个点的二分图。左边的每个点度数至少为
1
1
1 至多为
3
3
3,且左边每个点只会连向右边编号大于等于它的点。
现在你要选择一些边,限制如下:
- 右边的每一个点都要被覆盖到;
- 有一个 01 矩阵 A n × n A_{n\times n} An×n,若 A i , j = 1 A_{i,j}=1 Ai,j=1 则表示左边第 i i i 个点和第 j j j 个点不能同时被覆盖到;
- 对于左边的每一个点,如果它没被覆盖到则代价为 0 0 0,否则代价为 M i d i M_i^{d_i} Midi,其中 d i d_i di 表示被它覆盖了多少次。满足上面两条的情况下要使得代价最小。
求最小代价,或输出无解。
n
≤
18
,
1
≤
M
i
≤
100
n \leq 18,~1 \leq M_i \leq 100
n≤18, 1≤Mi≤100
多测,
T
≤
10
T \leq 10
T≤10
1s
\\
\\
\\
题解
考虑状压,首先会想到一个比较暴力的状压,设 f i , s L , s R f_{i,s_L,s_R} fi,sL,sR 表示左边考虑了前 i i i 个点,左边已选点集为 s L s_L sL,右边已选点集为 s R s_R sR,的最小代价。
进而发现好难优化,好像无论如何都要记录两个集合?
然后就需要发现题目的性质:考虑了左边前
i
i
i 个点的时候,左边后
n
−
i
n-i
n−i 个点一定未选;而由于左边的点只能连向右边编号大于等于它的点,所以右边前
i
i
i 个点必须全部已选(不然以后没机会了)。
因此,
s
L
s_L
sL 的后
n
−
i
n-i
n−i 位和
s
R
s_R
sR 的前
i
i
i 位都是没有用的!把
s
L
s_L
sL 的前
i
i
i 位和
s
R
s_R
sR 的后
n
−
i
n-i
n−i 位拼起来形成一个
2
n
2^n
2n 的状态,这样状压就是
O
(
n
⋅
2
n
)
O(n\cdot2^n)
O(n⋅2n) 的了!
这就很像计组里面学的 32 位整数乘除法,它把乘数和结果同时存在了一个 64 位整数上,这也是灵活地把两个状态叠在一起。
您好,请重修计组
代码
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
const int maxn=20, maxN=262150;
const int inf=2139062143;
int n,N,AA[maxn],v[maxn][4],v0[maxn],M[maxn],f[maxn][maxN],er[maxn];
bool G[maxn][maxn],A[maxn][maxn];
void ReadBit(bool &data)
{
char ch=getchar();
while (ch!='0' && ch!='1') ch=getchar();
data=(ch=='1');
}
inline void Min(int &a,const int &b) {a=(a<b) ?a :b ;}
int T;
int main()
{
er[0]=1;
fo(i,1,18) er[i]=er[i-1]<<1;
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
N=(1<<n)-1;
fo(i,1,n)
{
v0[i]=0;
fo(j,1,n)
{
ReadBit(G[i][j]);
if (G[i][j]) v[i][++v0[i]]=j;
}
}
fo(i,1,n)
{
AA[i]=0;
fo(j,1,n)
{
ReadBit(A[i][j]);
AA[i]|=A[i][j]<<(j-1);
}
}
fo(i,1,n) scanf("%d",&M[i]);
memset(f,127,sizeof(f));
f[0][0]=0;
fo(i,0,n-1)
fo(s,0,N) if (f[i][s]<inf)
{
int nowL=s&(er[i]-1), nowR=s^nowL, nxt=i+1;
bool alrdy=nowR&er[i];
int cho=s|er[i];
if (alrdy) Min(f[i+1][s^er[i]],f[i][s]);
if (AA[nxt]&nowL || v0[nxt]==0) continue;
if (alrdy || v[nxt][1]==nxt) Min(f[i+1][cho|er[v[nxt][1]-1]],f[i][s]+M[nxt]);
if (v0[nxt]>1)
{
if (alrdy || v[nxt][2]==nxt) Min(f[i+1][cho|er[v[nxt][2]-1]],f[i][s]+M[nxt]);
if (alrdy || v[nxt][1]==nxt || v[nxt][2]==nxt) Min(f[i+1][cho|er[v[nxt][1]-1]|er[v[nxt][2]-1]],f[i][s]+M[nxt]*M[nxt]);
}
if (v0[nxt]>2)
{
if (alrdy || v[nxt][3]==nxt) Min(f[i+1][cho|er[v[nxt][3]-1]],f[i][s]+M[nxt]);
if (alrdy || v[nxt][1]==nxt || v[nxt][3]==nxt) Min(f[i+1][cho|er[v[nxt][1]-1]|er[v[nxt][3]-1]],f[i][s]+M[nxt]*M[nxt]);
if (alrdy || v[nxt][2]==nxt || v[nxt][3]==nxt) Min(f[i+1][cho|er[v[nxt][2]-1]|er[v[nxt][3]-1]],f[i][s]+M[nxt]*M[nxt]);
if (alrdy || v[nxt][1]==nxt || v[nxt][2]==nxt || v[nxt][3]==nxt) Min(f[i+1][cho|er[v[nxt][1]-1]|er[v[nxt][2]-1]|er[v[nxt][3]-1]],f[i][s]+M[nxt]*M[nxt]*M[nxt]);
}
}
int ans=inf;
fo(s,0,N) Min(ans,f[n][s]);
printf("%d\n",(ans==inf) ?-1 :ans);
}
}