题目
展开 题目描述 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场。
圣域的地图可以看成是一个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 ( n \times m \le 50n×m≤50 且m\le nm≤n),表示矩阵的大小。
接下来一个n行m列的矩阵F,F_{i, j}F i,j 表示在城市(i,j)建造油库的代价。
输出格式 输出两个数,建造方案的油库个数和方案的总代价。
输入输出样例 输入 #1复制 3 3 6 5 4 1 2 3 7 8 9 输出 #1复制 3 6 说明/提示 对于30%数据满足 n \times m \le 25n×m≤25;
对于100%数据满足n \times m \le 50,0 \le F_{i, j} \le 100000n×m≤50,0≤F i,j ≤100000
思路
状压dp 设f[i][j][k]为第i行,第i行的状态为j,第i-1行的状态为k的最小代价 转移条件是:j∣k∣p∣(j≪1)∣(j≫1)&(2m−1)==(2m−1)
代码
#include<bits/stdc++.h>
using namespace std
;
int n
,m
;
int map
[51][51];
int dp
[51][129][129],s
[51][129][129];
int val
[51][129],num
[129];
int read()
{
char c
=getchar();
int x
=0;
while(c
<'0'||c
>'9') c
=getchar();
while(c
>='0'&&c
<='9')
x
=(x
<<3)+(x
<<1)+c
-48,c
=getchar();
return x
;
}
int logg(int x
)
{
int tot
=0;
while(x
) tot
++,x
>>=1;
return tot
;
}
int cnt(int x
)
{
int tot
=0;
while(x
) tot
++,x
-=lowbit(x
);
return tot
;
}
int M
;
int solve(int x
)
{
return M
&(((x
<<1)|x
)|((x
>>1)|x
));
}
void merge(int a
,int b
,int c
,int v
,int t
,int x
,int y
,int z
)
{
if(dp
[a
][b
][c
]+v
>dp
[x
][y
][z
]) return;
if(dp
[a
][b
][c
]+v
<dp
[x
][y
][z
])
{
dp
[x
][y
][z
]=dp
[a
][b
][c
]+v
;
s
[x
][y
][z
]=s
[a
][b
][c
]+t
;
return;
}
s
[x
][y
][z
]=min(s
[x
][y
][z
],s
[a
][b
][c
]+t
);
}
int main()
{
n
=read(),m
=read();
for(int i
=1; i
<=n
; i
++)
for(int j
=1; j
<=m
; j
++)
map
[i
][j
]=read();
M
=(1<<m
)-1;
for(int i
=1; i
<=n
; i
++)
for(int j
=1; j
<=M
; j
++)
val
[i
][j
]=val
[i
][j
-lowbit(j
)]+map
[i
][logg(lowbit(j
))];
for(int i
=1; i
<=M
; i
++) num
[i
]=cnt(i
);
memset(dp
,20,sizeof(dp
));
for(int i
=0; i
<=M
; i
++)
dp
[1][i
][solve(i
)]=min(dp
[1][i
][solve(i
)],val
[1][i
]),s
[1][i
][solve(i
)]=num
[i
];
for(int i
=2; i
<=n
; i
++)
{
for(int j
=0; j
<=M
; j
++)
{
int p
=M
^j
;
for(int k
=p
; k
; k
=(k
-1)&p
)
{
if(dp
[i
-1][j
][k
|j
]==336860180) continue;
int d
=k
|j
,s
=M
^d
;
for(int t
=d
; t
; t
=(t
-1)&d
)
merge(i
-1,j
,d
,val
[i
][s
]+val
[i
][t
],num
[s
]+num
[t
],i
,t
|s
,j
|solve(t
)|solve(s
));
merge(i
-1,j
,d
,val
[i
][s
],num
[s
],i
,s
,j
|solve(s
));
}
for(int k
=0; k
>-1; k
--)
{
if(dp
[i
-1][j
][k
|j
]==336860180) continue;
int d
=k
|j
,s
=M
^d
;
for(int t
=d
; t
; t
=(t
-1)&d
)
merge(i
-1,j
,d
,val
[i
][s
]+val
[i
][t
],num
[s
]+num
[t
],i
,t
|s
,j
|solve(t
)|solve(s
));
merge(i
-1,j
,d
,val
[i
][s
],num
[s
],i
,s
,j
|solve(s
));
}
}
}
int ans
=999999999;
for(int i
=0; i
<=M
; i
++)
ans
=min(ans
,dp
[n
][i
][M
]);
int T
=999999999;
for(int i
=0; i
<=M
; i
++)
if(dp
[n
][i
][M
]==ans
) T
=min(T
,s
[n
][i
][M
]);
printf("%d %d\n",T
,ans
);
return 0;
}