题意为给定一个二分图,左右点数均为n,并给定左右相连的边,对这些边两端点
现给定你一些限制条件并要求你选定一些点及其边,使得右边的点全部被覆盖到
状态压缩dp:
设以数组dp[i][j]
其中
vi v[20], m(30);
LL dp[20][(1 << 20) - 1], tt, n, b[20];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> tt;
f(sb, 1, tt)
{
string s;
f(i, 1, 20)
{
v[i].clear();
b[i] = 0;
}
cin >> n;
f(i, 1, n)
{
cin >> s;
f(j, 1, n)
{
if (s[j - 1] == '1')
v[i].pb(j);
}
}
f(i, 1, n)
{
cin >> s;
f(j, 1, n)
{
if (s[j - 1] == '1')
b[i] |= (1 << (n-j));
}
}
f(i, 1, n) cin >> m[i];
f(i, 1, n+1)
{
for (int j = 0; j < (1 << n); j++)
{
dp[i][j] = 2e8;
}
}
dp[1][0] = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < (1 << n); j++)
{
if (dp[i][j] == 2e8)
continue;
LL rs=(j&((1<<(n-i+1))-1)),ls=j&(((1<<n)-1)-((1<<(n-i+1))-1));
if ((rs >> (n - i)) & 1)
dp[i + 1][j ^ (1 << (n - i))] = min(dp[i][j], dp[i + 1][j ^ (1 << (n - i))]);
if (ls & b[i])
{
continue;
}
for (LL ds = 1; ds < (1 << (v[i].size())); ds++)
{
LL cost = 1, es = rs;
for (int k = 0; k < v[i].size(); k++)
{
if ((ds >> k) & 1)
cost *= m[i], es |= (1 <<(n- v[i][k]));
}
if (!((es >> (n - i)) & 1))
continue;
dp[i + 1][ls | es] = min(dp[i + 1][ls | es], dp[i][j] + cost);
}
}
}
LL ans = 2e8;
for (int i = 0; i < (1 << n); i++)
{
ans = min(ans, dp[n+1][i]);
}
cout << (ans == 2e8 ? -1 : ans) << "\n";
}
return 0;
}