拯救莫莉斯

Posted Hzoi_Maple

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了拯救莫莉斯相关的知识,希望对你有一定的参考价值。

一道考试题:然而谁会想到我考试的时候打了一个最小费用最大流??

真是弱爆了。。。

            拯救莫莉斯

                时间限制: 1 Sec  内存限制: 256 MB

题目描述

问题描述

莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场。

圣域的地图可以看成是一个n*m的矩阵。每个整数坐标点(x , y)表示一座城市(1<=x<= n, 1<=y<=m)。两座城市间相邻的定义为:对于城市(Ax, Ay)和城市(Bx, By),满足(Ax - Bx)2 + (Ay - By)2 = 1。

由于圣域的石油贸易总量很大,莫莉斯意识到不能让每笔石油订购单都从同一个油库里发货。为了提高效率,莫莉斯·乔决定在其中一些城市里建造油库,最终使得每一个城市X都满足下列条件之一:

1.该城市X内建有油库,

2.某城市Y内建有油库,且城市X与城市Y相邻。

与地球类似,圣域里不同城市间的地价可能也会有所不同,所以莫莉斯想让完成目标的总花费尽可能少。如果存在多组方案,为了方便管理,莫莉斯会选择建造较少的油库个数。

输入格式

第一行两个正整数n,m ( n * m <= 50 且m<=n),表示矩阵的大小。

接下来一个n行m列的矩阵F,Fi, j表示在城市(i,j)建造油库的代价。

输出格式

输出两个数,建造方案的油库个数和方案的总代价。

 

输入样例:

输出样例:

3 3

6 5 4

1 2 3

7 8 9

3 6

数据范围

对于30%数据满足 n * m <= 25;

对于100%数据满足n * m <= 50; 0 <= Fi, j <= 100000

因为是考试题,一定要详细点::

  首先读到题时要认真审题,是n*m<=50!!当你真真正正的理解题了后,忽然发现,这就是个状压啊~

设f[i][j][k]数组存储的是最小的花费,第一维代表前i行,第二维代表上一行的状态,第三维代表这一行的状态。

预处理出cost[i][j]数组代表在第i行时选取状态j时的花费;

所以很容易得出状态转移方程:f[i][k][u]=min(f[i][j][k]+cost[i][u]);

经过严密的调查发现因为每一个城市至少要有一个油库与之相邻,所以这个状态能够转移过来的前提是(i | j | u | k<<1| k>>1)&(1<<m)-1==(1<<m)-1

k代表当前这一行。这里一定要注意优先级!!

因为转移到第n行时有两维状态不确定,所以我们转移到第n+1行;

在转移f数组时同时维护一个g数组代表最小个数就好了!!

  希望下回能AC( ⊙ o ⊙ )啊!(来自蒟蒻的祈望~~)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 int read(){
 7     int sum=0;char ch=getchar();
 8     while(ch<0||ch>9) ch=getchar();
 9     while(ch>=0&&ch<=9){sum=sum*10+ch-0;ch=getchar();}
10     return sum;
11 }
12 int n,m;
13 int v[52][52],cost[52][1<<8],sum[1<<8];
14 int f[52][1<<8][1<<8],g[52][1<<8][1<<8];
15 inline int get(int zt){
16     int num;
17     for(num=0;(1<<num)<=zt;++num);
18     return num;
19 }
20 inline int get_sum(int zt){
21     int num=0;
22     while(zt){
23         if(zt&1) num++;
24         zt>>=1;
25     }
26     return num;
27 }
28 void St(){
29     for(int i=1;i<=n;++i)
30         for(int j=1;j<1<<m;++j){
31             int p=get(j);
32             cost[i][j]=cost[i][j^(1<<p-1)]+v[i][p];
33         }
34     for(int i=0;i<1<<m;++i)
35         sum[i]=get_sum(i);
36 }
37 void Dp(){
38     for(int i=0;i<1<<m;++i){
39         f[1][0][i]=cost[1][i];
40         g[1][0][i]=sum[i];
41     }
42     for(int i=1;i<=n;++i)
43         for(int j=0;j<1<<m;++j)//上一列的状态
44             for(int u=0;u<1<<m;++u){//这一列的状态
45                 if(f[i][j][u]>100000000) continue;
46                 for(int p=0;p<1<<m;++p)//下一列的状态
47                     if(((j|u|p|(u>>1)|(u<<1))&((1<<m)-1))==((1<<m)-1)){
48                         if(f[i+1][u][p]>=f[i][j][u]+cost[i+1][p]){
49                             if(f[i+1][u][p]==f[i][j][u]+cost[i+1][p]){
50                                 g[i+1][u][p]=min(g[i+1][u][p],g[i][j][u]+sum[p]);
51                             }
52                             else{
53                                 f[i+1][u][p]=f[i][j][u]+cost[i+1][p];
54                                 g[i+1][u][p]=g[i][j][u]+sum[p];
55                             }
56                         }
57                     }
58                 }
59 }
60 int main(){
61     freopen("proj.in","r",stdin);
62     freopen("proj.out","w",stdout);
63     memset(f,0x3f,sizeof(f));
64     n=read();m=read();
65     for(int i=1;i<=n;++i)
66         for(int j=1;j<=m;++j)
67             v[i][j]=read();
68     St();Dp();
69     int ans=0x7fffffff,res=0x7fffffff;
70     for(int i=0;i<1<<m;++i){
71         if(ans>=f[n+1][i][0]){
72             if(ans==f[n+1][i][0])
73                 res=min(res,g[n+1][i][0]);
74             else
75                 res=g[n+1][i][0];
76             ans=f[n+1][i][0];
77         }
78     }
79     printf("%d %d\n",res,ans);
80     return 0;
81 }

 

  

以上是关于拯救莫莉斯的主要内容,如果未能解决你的问题,请参考以下文章

拯救莫莉斯

[GDOI2014]拯救莫莉斯

拯救莫莉斯题解

拯救莫莉斯[GDOI2014]

GDOI2014 拯救莫莉斯

GDOI2014 拯救莫莉斯