zoukankan      html  css  js  c++  java
  • [HNOI2013]切糕

    题目描述

    经过千辛万苦小 A 得到了一块切糕,切糕的形状是长方体,小 A 打算拦腰将切糕切成两半分给小 B。出于美观考虑,小 A 希望切面能尽量光滑且和谐。于是她找到你,希望你能帮她找出最好的切割方案。

    出于简便考虑,我们将切糕视作一个长 P、宽 Q、高 R 的长方体点阵。我们将位于第 z层中第 x 行、第 y 列上(1≤x≤P, 1≤y≤Q, 1≤z≤R)的点称为(x,y,z),它有一个非负的不和谐值 v(x,y,z)。一个合法的切面满足以下两个条件:

    1. 与每个纵轴(一共有 P*Q 个纵轴)有且仅有一个交点。即切面是一个函数 f(x,y),对于所有 1≤x≤P, 1≤y≤Q,我们需指定一个切割点 f(x,y),且 1≤f(x,y)≤R。

    2. 切面需要满足一定的光滑性要求,即相邻纵轴上的切割点不能相距太远。对于所有的 1≤x,x’≤P 和 1≤y,y’≤Q,若|x-x’|+|y-y’|=1,则|f(x,y)-f(x’,y’)| ≤D,其中 D 是给定的一个非负整数。 可能有许多切面f 满足上面的条件,小A 希望找出总的切割点上的不和谐值最小的那个。

    输入输出格式

    输入格式:

    第一行是三个正整数P,Q,R,表示切糕的长P、 宽Q、高R。第二行有一个非负整数D,表示光滑性要求。接下来是R个P行Q列的矩阵,第z个 矩阵的第x行第y列是v(x,y,z) (1<=x<=P, 1<=y<=Q, 1<=z<=R)。 100%的数据满足P,Q,R<=40,0<=D<=R,且给出的所有的不和谐值不超过1000。

    输出格式:

    仅包含一个整数,表示在合法基础上最小的总不和谐值。

    输入输出样例

    输入样例#1:
    2  2 2
    1
    6  1
    6  1
    2  6
    2  6
    输出样例#1:
    6

    将模型简化一下,变成对于两个相邻点(x1,y1)和(x2,y2)的最小和谐值,如图
    由于答案是最小,所以考虑最小割
    因为一个点只选一层和谐值,所以考虑每相邻楼层建边,每个(x,y)形成一个长为r的路径
    此处为方便处理,先再度入时给节点标号,为nu[i][j][k](num重名)
    E=(nu[i][j][k],nu[i][j][k+1],w[i][j][k])
    接下来考虑怎么使相邻高度差不大于d
    如图所示,因为条件是相邻,所以只要与四个方向点(x',y')对应的路径建边
    如何建边?要使选择了(x,y)对应路径的第k号路的相邻路径只能选第[k-d,k+d]条
    只要加边(nu[i][j][k],nu[x][y][k-d],inf) 这条边不能被割
    如果割边选的是第[1,k-d-1]或[k+d+1,r]流都会通过新加边到达T,与割定义不符
    所以在这样的模型下跑一边最大流就行了

    给出一种减少超时的方法(见下红代码)
    解释:
    事先复制head数组(链式前向星)为cur,之后重点在“&”号上面,它会使cur随i的变化而变化
    也就是说,这条边遍历了,求出了流,下一次就会跳过遍历过的边
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<queue>
      6 using namespace std;
      7 struct Node
      8 {
      9     int next,to,dis;
     10 }edge[2000001];
     11 int num,head[1000001],n,m,d,r,nu[51][51][51],dist[1000001],cnt,ans,cur[1000001];
     12 const int dx[5]={0,1,-1,0,0};
     13 const int dy[5]={0,0,0,1,-1};
     14 void add(int u,int v,int dis)
     15 {
     16     //cout<<u<<' '<<v<<' '<<dis<<endl;
     17     edge[num].next=head[u];
     18     edge[num].to=v;
     19     edge[num].dis=dis;
     20     head[u]=num++;
     21     
     22     edge[num].next=head[v];
     23     edge[num].to=u;
     24     edge[num].dis=0;
     25     head[v]=num++;
     26 }
     27 bool bfs(int S,int T)
     28 {
     29     int i;
     30     memset(dist,-1,sizeof(dist));
     31     queue<int>Q;
     32     Q.push(S);
     33     dist[S]=1;
     34     while (!Q.empty())
     35     {
     36         int u=Q.front();
     37         Q.pop();
     38          for (i=head[u];i!=-1;i=edge[i].next)
     39          {
     40             int v=edge[i].to;
     41              if (edge[i].dis>0&&dist[v]==-1)
     42              {
     43                 dist[v]=dist[u]+1;
     44                 Q.push(v);
     45              }
     46          }
     47     }
     48      if (dist[T]==-1) return 0;
     49        return 1;
     50 }
     51 int dfs(int x,int flow,int des)
     52 {
     53     int res=0;
     54     //cout<<x<<endl;
     55      if (flow<=0||x==des) return flow;
     56      for (int &i=cur[x];i!=-1;i=edge[i].next)
     57      {
     58          if (dist[edge[i].to]==dist[x]+1&&edge[i].dis>0)
     59          {
     60              int tmp=dfs(edge[i].to,min(flow-res,edge[i].dis),des);
     61              if (tmp<=0) continue;
     62              edge[i^1].dis+=tmp;
     63              edge[i].dis-=tmp;
     64               res+=tmp;
     65               if (res==flow) return res;
     66          }
     67      }
     68 return res;
     69 }
     70 int main()
     71 {int k,i,j,p,v;
     72     cin>>n>>m>>r;
     73     cnt=0;
     74     cin>>d;
     75     memset(head,-1,sizeof(head));
     76   for (k=1;k<=r+1;k++)
     77   {
     78     for (i=1;i<=n;i++)
     79     {
     80       for (j=1;j<=m;j++)
     81        {
     82          nu[i][j][k]=++cnt;
     83        }  
     84     }
     85   }
     86   
     87    for (i=1;i<=n;i++)
     88    {
     89     for (j=1;j<=m;j++)
     90      add(0,nu[i][j][1],2e9),add(nu[i][j][r+1],cnt+1,2e9);
     91    }
     92     
     93 for (k=1;k<=r;k++)
     94   {
     95     for (i=1;i<=n;i++)
     96     {
     97       for (j=1;j<=m;j++)
     98        {
     99         scanf("%d",&v);
    100          add(nu[i][j][k],nu[i][j][k+1],v);
    101        }  
    102     }
    103   }
    104   for (i=d+1;i<=r;i++)
    105    {
    106     for (j=1;j<=n;j++)
    107      {
    108         for (k=1;k<=m;k++)
    109         {
    110             for (p=1;p<=4;p++)
    111             {
    112                 int x=j+dx[p],y=k+dy[p];
    113                 if (x&&y&&x<=n&&y<=m)
    114                 {
    115                   add(nu[j][k][i],nu[x][y][i-d],2e9);
    116                 }
    117             }
    118         }
    119      }
    120    }
    121     while (bfs(0,cnt+1))
    122     {
    123         int a=0;
    124         memcpy(cur,head,sizeof(cur));
    125         while (a=dfs(0,2e9,cnt+1)) ans+=a;
    126     }
    127     cout<<ans;
    128 }
  • 相关阅读:
    py程序----两个判断回文的程序
    Python特性
    python-基本数据类型
    shell编程第一天
    iptables防火墙
    纤维参数测量
    线性代数及其应用(最小二乘、PCA、SVD)
    水流方向检测
    微信跳一跳-MATLAB实现
    相机标定opencv实现
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/7392411.html
Copyright © 2011-2022 走看看