省选专练S4困难重重


题如其名难啊。

第一这个题数据非常诱惑人,有点像状压DP

的确,对于28分的数据可以使用插头DP(O(2^m*n^2*m^2))

但是爆搜都TMMMP的16分啊(O(4^(nm)))

然后考虑子任务。

有20%的数据是打01

这TM水吧。

CDOJ1432.摆箱子,和这样的数据几乎一样。

在矩阵中放入1*2的骨牌,骨牌之间不能相交,最大化骨牌数量

黑白染色后求解二分图最大匹配
O(nm)

期望得分:20

然后这实际是一个启示:这是一道网络流啊!

貌似有收费这个选项,于是考虑费用流(事实上这句话不太对,因为也有可能是最大权闭合图)

若A=B

好做吧!(*^▽^*)

我们枚举这个收费图形中心点。

若一个点度数为k(0<=k<=4),则其贡献了k*(k-1)/2个收费的图案。

还记得有一种做平方费用的费用流嘛?

他如何做的?拆点利用流过去必然是Cost较小的原理。

费用递增模型,连4条边(1,0)(1,A)(1,2A),(1,3A)
黑白染色后求最小费用流,每次增广一条路
O(n^2*m^2)

期望得分:48

好的再次观察数据:

A<=B

凭什么?于是我们考虑先贪心搞弯的,然后把弯的掰直(Cost==B-A)

于是拆点,拆三个。

一个点用来控制流量(点的度数小于4)。

一个点表示弯的,一个点表示直的。

处理A,费用递增模型,x和S或T连4条边(1,0)(1,A)(1,2A)(1,3A)

处理B-A,同样是费用递增模型,x和x1连2条边(1,0)(1,B-A),x和x2连2条边(1,0)(1,B-A)

最小费用流,每次增广一条路,这是什么意思,你不需要跑最小费用流,而是For循环跑当前费用。
O(n^2*m^2)
期望得分:100

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef int INT;
#define int long long
const int N=90000;
const int INF=1e16;
struct Front_star{
	int u,v,w,c,nxt;
}e[N*4];
int cnt=1;
int first[N]={0};
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void addedge(int u,int v,int w,int c){
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].c=c;
	e[cnt].nxt=first[u];
	first[u]=cnt;
}
void add(int u,int v,int w,int c){
	addedge(u,v,w,c);
	addedge(v,u,0,-c);
}
queue<int> q;
int inqueue[N]={0};
int dis[N]={0};
int pre[N]={0};
int S=0;
int T;
bool SPFA(){
	for(int i=S;i<=T;i++){
		dis[i]=INF;
		pre[i]=0;
	}
	dis[S]=0;
	q.push(S);
	while(!q.empty()){
		int x=q.front();
		q.pop();
//		cout<<x<<" <-"<<endl;
		inqueue[x]=0;
		for(int i=first[x];i;i=e[i].nxt){
			int v=e[i].v;
			if(e[i].w&&e[i].c+dis[x]<dis[v]){
				dis[v]=dis[x]+e[i].c;	
				pre[v]=i;
				if(!inqueue[v]){
					q.push(v);
					inqueue[v]=1;
				}	
			}
		}	
	}
}
int n,m,A,B;
int g[120][120]={0};
int edge[120][120][5]={0};
INT main(){
	int Type;
	scanf("%lld",&Type);
	scanf("%lld%lld%lld%lld",&n,&m,&A,&B);
	T=n*m*3+100;
	for(int i=1;i<=n;i++){
		char ch[100];
		scanf("%s",ch);
		for(int j=0;j<m;j++){
			g[i][j+1]=ch[j]-'0';
		}
	}
	int all=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int k=1;k<=3;k++){
				edge[i][j][k]=++all;
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(g[i][j]==0){
				if((i+j)%2==1){
					add(S,edge[i][j][1],1,0);
					add(S,edge[i][j][1],1,A);
					add(S,edge[i][j][1],1,2*A);
					add(S,edge[i][j][1],1,3*A);
					add(edge[i][j][1],edge[i][j][2],1,0);
					add(edge[i][j][1],edge[i][j][2],1,B-A);
					add(edge[i][j][1],edge[i][j][3],1,0);
					add(edge[i][j][1],edge[i][j][3],1,B-A);
				}
				else{
					add(edge[i][j][1],T,1,0);
					add(edge[i][j][1],T,1,A);
					add(edge[i][j][1],T,1,2*A);
					add(edge[i][j][1],T,1,3*A);
					add(edge[i][j][2],edge[i][j][1],1,0);
					add(edge[i][j][2],edge[i][j][1],1,B-A);
					add(edge[i][j][3],edge[i][j][1],1,0);
					add(edge[i][j][3],edge[i][j][1],1,B-A);
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if((i+j)%2==1&&g[i][j]==0){
				for(int k=0;k<4;k++){
					int nx=i+dx[k];
					int ny=j+dy[k];
					if(g[nx][ny]==0){
						if(k<2){
							add(edge[i][j][2],edge[nx][ny][2],1,0);
						}
						else{
							add(edge[i][j][3],edge[nx][ny][3],1,0);
						}
					}
				}
			}
		}
	}
	int Q;
//	for(int i=2;i<=cnt;i++){
//		cout<<e[i].u<<" "<<e[i].v<<endl;
//	}
//	cout<<T<<" towards"<<endl;
	scanf("%lld",&Q);
	int ret=0;
	while(Q--){
		SPFA();
		int s=1;
		for(int i=pre[T];i;i=pre[e[i^1].v]){
			e[i].w-=s;
			e[i^1].w+=s;
		}
//		cout<<s<<endl;
		ret+=s*dis[T];
		cout<<((Type>=8&&Type<=12)?ret>=1:ret)<<'\n';
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值