题解—— [GDOI2014]拯救莫莉斯 状压DP

题面:

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

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

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

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

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

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

n * m <= 50, m < n

简单题意:

有一个带权值的矩阵,取一个方格的代价为它的权值,取一个方格时可以给它自己和上下左右的格子打上标记,求最小代价(代价相同取最少数量的方格)使得整个矩阵都被标记。

题解:

观察到n * m <=50,m < n,也就是说m最大也只能是7,看见这么小的数,,,显然这就是个状压啊!

第一眼貌似就是很套路的状压,,,

不过貌似还是有不同的,

唯一的不同在于:一行受上一行和下一行的同时影响,也就是说当前行可以不满足全都标记,因为后来还可以有别的城市来标记它,

因此枚举i和i-1和i-2行的状态,合法条件为:必须使得i-1合法,因此i-1要是再不合法的话以后都不能合法了,,,

同时将f初始化为极大值,这样的话就无需判断i-2是否合法,因为不合法的话将不会被更新,那么值就是inf,也就不会被当做决策了

但是观察到我们并不方便临时计算每个状态的各种数据,因此我们先预处理一遍,处理出每一行的每一个状态对应的城市数和代价,

分别记为num[i][j].num 和 num[i][j].cost

设f[i][j][k].cost 为第i行状态为j,第i-1行状态为k的最小代价,f[i][j][k].num表示在最小代价的基础上的最少城市数,

那么我们的合法条件显然为:if(((k | j | l | (k << 1) | (k >> 1)) & all) == all),其中k为i-1行,j为i行,l为i-2行,

all为2 ^ m - 1 , 也就是2进制下的 111111(m个1)

& all用于消除高于m位的1,以免对ans产生影响。

如果==all就表示合法,因为| j 和| l 表示用上下的城市来标记k一次,然后k << 1 和k >> 1就是用左右的城市来标记一次

那么有转移方程:

if(f[i-1][k][l] + num[i][j] <= f[i][j][k])

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

其中

 1 node operator + (node a,node b)
 2 {
 3     a.cost += b.cost;
 4     a.num += b.num;
 5     return a;
 6 }
 7
 8 bool operator <= (node a,node b)
 9 {
10     if(a.cost < b.cost) return true;
11     else if(a.cost == b.cost && a.num < b.num) return true;
12     else return false;
13 }

最后统计ans的时候的合法条件为第n行合法。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 300
 5 #define inf 80000000
 6 #define ac 50
 7 int n,m,all;
 8 int s[ac][ac];
 9 struct node{
10     int cost,num;
11 }f[ac][AC][AC],num[ac][AC],ans;
12 /*因为n * m <= 50 , m < n,所以m实际上是很小的,m <= sqrt(50),
13 因此预处理出对于每一行,任意状态下的油库个数和代价,在代价相同的基础上取油库最少
14 一定要注意状态从0开始枚举!*/
15 node operator + (node a,node b)
16 {
17     a.cost += b.cost;
18     a.num += b.num;
19     return a;
20 }
21
22 bool operator <= (node a,node b)
23 {
24     if(a.cost < b.cost) return true;
25     else if(a.cost == b.cost && a.num < b.num) return true;
26     else return false;
27 }
28
29 void pre()
30 {
31     scanf("%d%d",&n,&m);
32     all=(1 << m) - 1;
33     for(R i=1;i<=n;i++)
34         for(R j=1;j<=m;j++)
35             scanf("%d",&s[i][j]);
36     for(R i=1;i<=n;i++)//枚举行,看做常数?
37         for(R j=0;j<=all;j++)//枚举状态
38             for(R k=1;k<=m;k++)//看做常数?
39                 if(j & (1 << (m - k))) //预处理,,,但是看上去复杂度很高啊
40                 {
41                     num[i][j].cost+=s[i][k];
42                     num[i][j].num++;
43                 }
44     ans.cost=inf , ans.num=inf;
45 }
46
47 void work()
48 {
49     for(R i=0;i<=all;i++) f[1][i][0]=num[1][i];
50     for(R i=0;i<=all;i++)
51         for(R j=0;j<=all;j++)
52         {
53             f[2][i][j].cost = inf;
54             if(((j | i | (j << 1) | (j >> 1)) & all) == all)
55                 f[2][i][j]=f[1][j][0] + num[2][i];
56         }
57     for(R i=3;i<=n;i++)//枚举行
58         for(R j=0;j<=all;j++)//枚举当前行
59             for(R k=0;k<=all;k++)//枚举上一行状态
60             {
61                 f[i][j][k].cost = inf;
62                 f[i][j][k].num = inf;
63                 for(R l=0;l<=all;l++)//枚举上上行状态
64                     if(((k | j | l | (k << 1) | (k >> 1)) & all) == all)//全都更新一遍,至于如何解决二次传播的方法,,,用原版就好了啊
65                         if(f[i-1][k][l] +  num[i][j] <= f[i][j][k])
66                             f[i][j][k]=f[i-1][k][l] + num[i][j];
67             }
68     for(R i=0;i<=all;i++)
69     {
70         for(R j=0;j<=all;j++)
71             if(((i | j | (i << 1) | (i >> 1)) & all) == all)
72                 if(f[n][i][j] <= ans) ans=f[n][i][j];
73     }
74     printf("%d %d\n",ans.num,ans.cost);
75 }//因为受两行影响,而且是上下两行,,,,所以当前行不用满足,保证上一行满足即可???
76 //因为上一行还不满足的话就满足不了了
77 int main()
78 {
79 //    freopen("in.in","r",stdin);
80     pre();
81     work();
82 //    fclose(stdin);
83     return 0;
84 }

原文地址:https://www.cnblogs.com/ww3113306/p/9097349.html

时间: 2024-10-06 05:18:18

题解—— [GDOI2014]拯救莫莉斯 状压DP的相关文章

GDOI2014 拯救莫莉斯

问题描述 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场. 圣域的地图可以看成是一个n*m的矩阵.每个整数坐标点(x , y)表示一座城市(1<=x<= n, 1<=y<=m).两座城市间相邻的定义为:对于城市(Ax, Ay)和城市(Bx, By),满足(Ax - Bx)2 + (Ay - By)2 = 1. 由于圣域的石油贸易总量很大,莫莉斯意识到不能让每笔石油订购单都从同一个油库里发货.为了提高效率,莫莉斯·乔决定在其中一些城市里建造

拯救莫莉斯题解

拯救莫莉斯题解 实际上是水题啦, 观察数据发现,\(m*n<=50,m<=n\),所以\(m<=sqrt(50)\)即\(m<=7\) 只可能是状压啊,搜索啊,矩阵啊 但是矩阵一般有一项非常大,大到\(O(n)\)做不了,所以基本排除, 搜索基本不可能的,毕竟吗m*n还是有50的, 所以状压, 水啊, 上代码吧: #include<bits/stdc++.h> using namespace std; const int N=56,M=147; int n,m,t,p,

拯救莫莉斯

一道考试题:然而谁会想到我考试的时候打了一个最小费用最大流?? 真是弱爆了... 拯救莫莉斯 时间限制: 1 Sec  内存限制: 256 MB 题目描述 问题描述 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场. 圣域的地图可以看成是一个n*m的矩阵.每个整数坐标点(x , y)表示一座城市(1<=x<= n, 1<=y<=m).两座城市间相邻的定义为:对于城市(Ax, Ay)和城市(Bx, By),满足(Ax - Bx)2 + (Ay

拯救莫莉斯[GDOI2014]

时间限制:1s     内存限制:256MB 问题描述 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场. 圣域的地图可以看成是一个n*m的矩阵.每个整数坐标点(x , y)表示一座城市(1<=x<= n, 1<=y<=m).两座城市间相邻的定义为:对于城市(Ax, Ay)和城市(Bx, By),满足(Ax - Bx)2 + (Ay - By)2 = 1. 由于圣域的石油贸易总量很大,莫莉斯意识到不能让每笔石油订购单都从同一个油库里发货.为

题解——[USACO06NOV]玉米田Corn Fields 状压DP

题面: 农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地.John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用. 遗憾的是,有些土地相当贫瘠,不能用来种草.并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边. John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案) 输出一个整数,即牧

【题解】[SCOI2005] 互不侵犯 (状压DP)

[SCOI2005] 互不侵犯 终于懂一点状压DP了… 用一个数的二进制形式表示一整行的状态,比如 18(1010)表示第一列和第三列有国王. 然后用&判断是否可行: if((x&y)||((x<<1)&y)||(x&(y<<1))) continue;1code: #include<iostream>#include<cstdio>#include<cstring>#include<algorithm>

CF482C Game with Strings (状压DP+期望DP)

题目大意:甲和乙玩游戏,甲给出n(n<=50)个等长的字符串(len<=20),然后甲选出其中一个字符串,乙随机询问该字符串某一位的字符(不会重复询问一个位置),求乙能确定该串是哪个字符串的询问次数的期望值 这题不看题解好难想......(感谢zhx和zhx两位大佬的题解) len很小,考虑状压DP,显然我们要状压询问,要定义两个状态,f[]和num[] 1表示询问,0表示未询问 那么,我们定义f[s]表示询问状态s距离确定一个字符串所需要的期望值. 定义tot是s状态剩余的询问的次数,那么显

Codeforces Round #531 (Div. 3) F. Elongated Matrix(状压DP)

F. Elongated Matrix 题目链接:https://codeforces.com/contest/1102/problem/F 题意: 给出一个n*m的矩阵,现在可以随意交换任意的两行,最后从上到下,从左到右形成一个序列s1,s2.....snm,满足对于任意相邻的两个数,它们差的绝对值的最大值为k. 现在问怎么交换行与行,可以使得最后的这个k最大. 题解: 人生中第一道状压dp~其实还是参考了这篇博客:https://blog.csdn.net/CSDNjiangshan/art

luogu3888 GDOI2014拯救莫里斯 (状压dp)

题目描述 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场. 圣域的地图可以看成是一个n*m的矩阵.每个整数坐标点(x , y)表示一座城市\(( 1\le x\le n,1\le y\le m)\).两座城市间相邻的定义为:对于城市(Ax, Ay)和城市(Bx, By),满足 (Ax - Bx)^2 + (Ay - By)^2 = 1(Ax?Bx)2+(Ay?By)2=1 . 由于圣域的石油贸易总量很大,莫莉斯意识到不能让每笔石油订购单都从同一个油库里