软件补丁问题

题意

题目链接

想法

顺着做网络流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;
}
上一篇:P1985 [USACO07OPEN]翻转棋


下一篇:洛谷题解 P4392 【[BOI2007]Sound 静音问题】