真正的危机不是机器人像人一样思考,而是人像机器一样思考。 ——凉宫春日的忧郁

[GDOI2014]拯救莫莉斯

[GDOI2014]拯救莫莉斯

题目

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

圣域的地图可以看成是一个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相邻。

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

INPUT

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

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

OUTPUT

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

SAMPLE

INPUT

3 3

6 5 4

1 2 3

7 8 9

OUTPUT

3 6

解题报告

考试打的爆搜,本来能骗35分的,结果生给CE(编译错误)掉了,重要的是本机跑啥事没有,简直了

正解:

一看数据范围就知道是状压

首先分析数据范围,,显然可以推出,那么剩下的就很好办了,我们可以压每一行的状态

然后我们再看,第i行的状态只会对第i-1行至第i+1行产生影响

那么我们就只需要记录一下每一行和它上一行的状态就可以了

设:f[i][j][k]为对于前i行,第i-1行状态为j,第i行状态为k,对于任何一行x(x<i),该行的所有城市都能被供油的情况下,其所能达到的最小代价。

设:g[i][j][k]记录在上述条件下对应f[i][j][k]方案里的油库数量

然后我们可以预处理出每一行的状态所对应的代价与油库数量(设cost[i][j]为第i行,状态为j时所需代价,num[i][j]为第i行状态为j时的油库数量)

那么我们可以很轻松的写出状态转移方程:

f[i+1][k][l]=f[i][j][k]+cost[i+1][l];
g[i+1][k][l]=g[i][j][k]+num[i+1][l];

而其中,l状态可取,需使第i行的所有城市满足要求,即:

((j|l|k|(k<<1)|(k>>1))&(bin[m]-1))==bin[m]-1

那么答案就是 max(f[n+1][x][0]) 

这里需要注意的是,数组不能卡着数开,因为我们的答案存储在n+1中,所以最好多开一点,以防越界

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 inline int read(){
 6     int sum(0);
 7     char ch(getchar());
 8     for(;ch<'0'||ch>'9';ch=getchar());
 9     for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar());
10     return sum;
11 }
12 int n,m;
13 int bin[8];
14 int w[55][10];
15 int g[55][1<<7][1<<7],f[55][1<<7][1<<7],cost[55][1<<7],num[55][1<<7];
16 int main(){
17 //  freopen("proj.in","r",stdin);
18 //  freopen("proj.out","w",stdout);
19     memset(f,30,sizeof(f));
20     memset(cost,0,sizeof(cost));
21     bin[0]=1;
22     for(int i=1;i<8;i++)
23         bin[i]=bin[i-1]<<1;
24     n=read(),m=read();
25     for(int i=1;i<=n;i++)
26         for(int j=0;j<m;j++)
27             w[i][j]=read();
28     for(int i=1;i<=n;i++)
29         for(int j=0;j<bin[m];j++)
30             for(int k=0;k<m;k++)
31                 if(bin[k]&j){
32                     cost[i][j]+=w[i][k];
33                     num[i][j]++;
34                 }
35 /*  for(int i=1;i<=n;i++)
36         for(int j=0;j<bin[m];j++)
37             cout<<i<<' '<<j<<' '<<cost[i][j]<<endl;*/
38     for(int i=0;i<bin[m];i++)
39         f[0][i][0]=0;
40     for(int i=0;i<=n;i++)
41         for(int j=0;j<bin[m];j++)
42             for(int k=0;k<bin[m];k++)
43                 for(int l=0;l<bin[m];l++){
44                     if(((j|l|k|(k<<1)|(k>>1))&(bin[m]-1))!=bin[m]-1)
45                         continue;
46                     if(f[i+1][k][l]>f[i][j][k]+cost[i+1][l]){//cout<<i<<endl;
47                         f[i+1][k][l]=f[i][j][k]+cost[i+1][l];
48                         g[i+1][k][l]=g[i][j][k]+num[i+1][l];//cout<<i<<" "<<i+1<<' '<<k<<' '<<l<<' '<<f[i+1][k][l]<<endl;
49                     }
50                 }
51     int ans(0x7fffffff),ji;//cout<<m<<' '<<bin[m]<<endl;
52     for(int i=0;i<bin[m];i++){//cout<<i<<" "<<f[n+1][i][0]<<endl;
53         if(f[n+1][i][0]==ans)
54             if(ji>g[n+1][i][0])
55                 ji=g[n+1][i][0];
56         if(f[n+1][i][0]<ans){//cout<<i<<' '<<f[n+1][i][0]<<endl;
57             ans=f[n+1][i][0];
58             ji=g[n+1][i][0];
59         }
60     }
61     printf("%d %d",ji,ans);
62 }
View Code

 

posted @ 2017-08-06 19:02  Hzoi_Mafia  阅读(224)  评论(0编辑  收藏  举报
我们都在命运之湖上荡舟划桨,波浪起伏着而我们无法逃脱孤航。但是假使我们迷失了方向,波浪将指引我们穿越另一天的曙光。 ——死神