ABC219

D

至少装满型背包

每个东西有两个维度的体积,选一些东西,是的两个维度的体积和分别超过 x , y x,y x,y,问最少需要选多少个东西

这种至少装满,肯定不能把背包大小开到特大,这样虽然肯定能统计到所有情况,但是每次都枚举所有容积,复杂度太高了。

这个时候的正解是,既然我们只关心是否超过 x , y x,y x,y,可以只把背包大小开到 x , y x,y x,y,然后用刷表型 d p dp dp,把大于 x , y x,y x,y的值都映射到 x , y x,y x,y

最后 d p ( x , y ) dp(x,y) dp(x,y)相当于是存储了所有不小于 x , y x,y x,y的方案的最小代价

void solve(){
	int n,x,y;
	cin>>n>>x>>y;
	vi a(n+1),b(n+1);
	rep(i,1,n){
		cin>>a[i];
		cin>>b[i];
	}
	
	vvi dp(x+10,vi(y+10,1e18));
	dp[0][0]=0;
	rep(i,1,n){
		vvi ndp(x+10,vi(y+10,1e18));
		ndp=dp;
		rep(j,0,x){
			rep(k,0,y){
				int jj=min(x,j+a[i]);
				int kk=min(y,k+b[i]);
				ndp[jj][kk]=min(dp[j][k]+1,ndp[jj][kk]);
			}
		}
		swap(ndp,dp);
	}
	if(dp[x][y]<1e18)cout<<dp[x][y];
	else cout<<-1;
}

E

爆搜染色

一个网格图,围出一个区域,需要包含所有村庄,问有多少种不同方案。

开始由于题干是从区域边界定义的,我被误导了,虽然想到了枚举,但是想的是枚举边界的边,最后判定是否围起来所有村庄。但这个显然不可做。

后面才发现可以直接对网格染色,16个格子,直接用一个 2 16 2^{16} 216的爆搜枚举所有染色情况。

然后题目的约束是围出一个区域,所以染色区域必须只有一个连通块;而且染色区域内部不能有空洞,这点可以通过对所有未染色的点跑BFS,判断能否走到网格边界。

void solve(){
	vvi a(4,vi(4));
	int tot=0;
	rep(i,0,3){
		rep(j,0,3){
			cin>>a[i][j];
			tot+=a[i][j];
		}
	}
	vvi vis(4,vi(4));
	int ans=0;

	auto check=[&]()->bool{
		vvi vis0(4,vi(4));
		auto &&dfs0=[&](auto &&dfs0,int i,int j)->void{
			vis0[i][j]=1;
			rep(k,0,3){
				int ii=i+dx[k],jj=j+dy[k];
				if(ii<0||ii>3||jj<0||jj>3||vis0[ii][jj])continue;
				if(vis[ii][jj]){
					dfs0(dfs0,ii,jj);
				}
			}	
		};
		int cnt=0;
		rep(i,0,3){
			rep(j,0,3){
				if(!vis0[i][j]&&vis[i][j]){
					dfs0(dfs0,i,j);
					cnt++;
				}
			}
		}
		return cnt==1;
	};
	auto &&dfs=[&](auto &&dfs,int i,int j)->void{
		if(i==4){
			int cnt=0,out0=0,out=0;
			auto &&check1=[&](int x,int y)->int{
				queue<pii>q;
				q.push({x,y});
				vvi vis2(4,vi(4));
				vis2[x][y]=1;
				while(q.size()){
					auto t=q.front();
					q.pop();
					
					int x=t.fi,y=t.se;
					rep(i,0,3){
						int xx=x+dx[i],yy=y+dy[i];
						if(xx<0||xx>3||yy<0||yy>3){
							return 1;
						}
						if(!vis[xx][yy]&&!vis2[xx][yy]){
							vis2[xx][yy]=1;
							q.push({xx,yy});
						}
					}
				}
				return 0;
			};
			if(!check())return;
			rep(x,0,3){
				rep(y,0,3){
					if(vis[x][y]){
						cnt+=a[x][y];
					}
					else{
						out0++;
						out+=check1(x,y);
					}
					
				}
			}
			
			if(cnt==tot&&out==out0){
				ans++;
//				cout<<out<<' '<<out0<<'\n';
//				rep(i,0,3){
//					rep(j,0,3){
//						cout<<vis[i][j]<<' ';
//					}
//					cout<<'\n';
//				}
			}
			return ;
		}
		vis[i][j]=1;
		dfs(dfs,j==3?i+1:i,j==3?0:j+1);
		vis[i][j]=0;
		dfs(dfs,j==3?i+1:i,j==3?0:j+1);
	};
	dfs(dfs,0,0);
	cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值