软件补丁问题
题意
想法
顺着做网络流24题看到的 起初原本以为是费用流
后来看到\(n <= 20\)..
状压走起 这题状压还是很明显的,\(b1,b2,f1,f2\)都明显压缩,初始状态\((1 << n) - 1\) 目标状态\(0\)
这题因为点数太多,直接建边的话空间代价太大,我们选择对每个点都跑一个\(m\)次的枚举
当现在的状态\(now\)满足\((now\quad and\quad b1[i])\quad ==\quad b1[i]\quad and\quad (now\quad and\quad b2[i])\quad ==\quad 0\),\(i\)补丁是合法的
可以变为\(((now\quad or\quad f1[i])\quad xor\quad f1[i])\quad or\quad f2[i]\)
然后跑\(spfa\)和\(dij\)都行
代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define ll long long //注意数据范围 可以状压
using std::queue;
ll n,m;
ll b1[200],b2[200],f1[200],f2[200],v[200];
ll minn[(1 << 20) + 10];
bool vis[(1 << 20) + 10];
queue<ll>QWQ;
void spfa(){
memset(minn,0x7f,sizeof(minn));
memset(vis,0,sizeof(vis));
ll now = (1 << n) - 1;
minn[now] = 0;
QWQ.push(now);
while(!QWQ.empty()){
now = QWQ.front();
//std::cout<<now<<std::endl;
QWQ.pop();
vis[now] = 0;
for(int i = 1;i <= m;++i){
ll to = ((now | f1[i]) ^ f1[i]) | f2[i];
//std::cout<<to<<" "<<((now & b1[i]) == b1[i] && (now & b2[i]) == 0)<<std::endl;
if((now & b1[i]) == b1[i] && (now & b2[i]) == 0 && minn[to] > minn[now] + v[i]){
minn[to] = minn[now] + v[i];
if(!vis[to])vis[to] = 1,QWQ.push(to);
}
}
}
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i = 1;i <= m;++i){
char a[100],b[100];
scanf("%lld%s%s",&v[i],a,b);
for(int j = 0;j < n;++j){
if(a[j] == '+')
b1[i] |= (1 << j);
if(a[j] == '-')
b2[i] |= (1 << j);
if(b[j] == '-')
f1[i] |= (1 << j);
if(b[j] == '+')
f2[i] |= (1 << j);
}
}
// for(int i = 1;i <= m;++i)
// std::cout<<b1[i]<<" "<<b2[i]<<" "<<f1[i]<<" "<<f2[i]<<std::endl;
spfa();
if(minn[0] <= 20000000000)
std::cout<<minn[0]<<std::endl;
else
std::cout<<0<<std::endl;
}