
题如其名难啊。
第一这个题数据非常诱惑人,有点像状压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';
}
}
1778

被折叠的 条评论
为什么被折叠?



