[BZOJ3144][HNOI2013]切糕

总结一下这一题我的思考过程,以及与题解的对比。
这题我首先考虑二维平面的切法,发现就是一个最短路,然后套在三维的情况,发现无法保证每个 (x,y) 都经过。所以说这种思路是不对的。一开始就不对,后面怎么可能对。一般靠谱的思路自己都会觉得靠谱,我那样搞十分玄学肯定不是正解。
最小费用好像很玄学,考虑最小割吧,既然一个 (x,y) 只要选一个,我们把同一纵行串成一链,那么求每条链的最小割就行了。这样看起来挺靠谱的,于是就有了继续思考的勇气。
考虑限制条件怎么转化成网络流模型。
选了 x 在旁边的就只能选[xd,x+d],所以我们要对图做一些工,使得不满足条件的选法无法构成最小割。
经过一些尝试性的探索可以发现 x 向四个柱子的xd连一条INF就可以保证了,具体可以画图体验一下。意思就是说通过这些边,只有在限制内的区域割一条边才能保证割开,否则就可以通过这些边走到汇点。

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<sstream>
#include<climits>
#define X first
#define Y second
#define DB double
#define lc now<<1
#define rc now<<1|1
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
template<typename T>void Read(T& x)
{
    x=0;int flag=0,sgn=1;char c;
    while(c=getchar())
    {
        if(c=='-')sgn=-1;
        else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
        else if(flag)break;
    }
    x*=sgn;
}
const int MAXN=40*40*40+10;
int p,q,r,id[45][45][45],w[45][45][45],tot=0,d;
struct Edge{
    int u,v,cap,flow;
    Edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){}
};
struct Dinic{
    int cur[MAXN],dis[MAXN],vis[MAXN],S,T;
    vector<int> G[MAXN];
    vector<Edge> edges;
    void add(int u,int v,int c)
    {
        edges.pb(Edge(u,v,c,0));
        edges.pb(Edge(v,u,0,0));
        int m=edges.size();
        G[u].pb(m-2);
        G[v].pb(m-1);
    }
    bool BFS()
    {
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(S);
        dis[S]=1;
        vis[S]=1;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=0;i<G[u].size();i++)
            {
                Edge& e=edges[G[u][i]];
                if(!vis[e.v]&&e.cap-e.flow)
                {
                    dis[e.v]=dis[u]+1;
                    vis[e.v]=1;
                    q.push(e.v);
                }
            }
        }
        return vis[T];
    }
    int DFS(int x,int a)
    {
        if(x==T||a==0)return a;
        int f=0,flow=0;
        for(int& i=cur[x];i<G[x].size();i++)
        {
            Edge& e=edges[G[x][i]];
            if(dis[e.v]==dis[x]+1&&(f=(DFS(e.v,min(a,e.cap-e.flow)))>0))
            {
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                a-=f;
                flow+=f;
                if(a==0)break;
            }
        }
        return flow;
    }
    int maxflow()
    {
        int mx=0;
        while(BFS())
        {
            memset(cur,0,sizeof(cur));
            mx+=DFS(S,INF);
        }
        return mx;
    }
}Graph;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("cake.in","r",stdin);
    freopen("cake.out","w",stdout);
#endif
    Read(p),Read(q),Read(r),Read(d);
    for(int i=1;i<=r;i++)
        for(int j=1;j<=p;j++)
            for(int k=1;k<=q;k++)
                Read(w[i][j][k]),id[i][j][k]=++tot;
    Graph.S=++tot,Graph.T=++tot;
    for(int i=1;i<=p;i++)
        for(int j=1;j<=q;j++)
        {
            for(int k=r;k>=1;k--)
            {
                if(k==r)
                    Graph.add(Graph.S,id[k][i][j],w[k][i][j]);
                else
                    Graph.add(id[k+1][i][j],id[k][i][j],w[k][i][j]);
                if(k+d<=r)
                {
                    if(i>1)
                        Graph.add(id[k][i][j],id[k+d][i-1][j],INF);
                    if(i<p)
                        Graph.add(id[k][i][j],id[k+d][i+1][j],INF);
                    if(j>1)
                        Graph.add(id[k][i][j],id[k+d][i][j-1],INF);
                    if(j<q)
                        Graph.add(id[k][i][j],id[k+d][i][j+1],INF);
                }
                if(k==1)
                    Graph.add(id[k][i][j],Graph.T,INF);
            }
        }
    cout<<Graph.maxflow()<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值