题目描述
莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场。
圣域的地图可以看成是一个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 ( A x − B x ) 2 + ( A y − B y ) 2 = 1
由于圣域的石油贸易总量很大,莫莉斯意识到不能让每笔石油订购单都从同一个油库里发货。为了提高效率,莫莉斯·乔决定在其中一些城市里建造油库,最终使得每一个城市X都满足下列条件之一:
1.该城市X内建有油库,
2.某城市Y内建有油库,且城市X与城市Y相邻。
与地球类似,圣域里不同城市间的地价可能也会有所不同,所以莫莉斯想让完成目标的总花费尽可能少。如果存在多组方案,为了方便管理,莫莉斯会选择建造较少的油库个数。
输入输出格式
输入格式:
第一行两个正整数n,m
(n×m≤50,n×m≤50且m≤n,m≤n)
(
n
×
m
≤
50
,
n
×
m
≤
50
且
m
≤
n
,
m
≤
n
)
,表示矩阵的大小。
接下来一个n行m列的矩阵F, Fi,j F i , j 表示在城市(i,j)建造油库的代价。
输出格式:
输出两个数,建造方案的油库个数和方案的总代价。
其中数据范围是
对于30%数据满足 n×m≤25,n×m≤25; n × m ≤ 25 , n × m ≤ 25 ;
对于100%数据满足 n×m≤50,0≤Fi,j≤100000,n×m≤50,0≤Fi,j≤100000 n × m ≤ 50 , 0 ≤ F i , j ≤ 100000 , n × m ≤ 50 , 0 ≤ F i , j ≤ 100000
看一下这个数据范围,不难得出,m<=7
所以可以直接进行dp
首先我们先预处理一下 cost[i][j] c o s t [ i ] [ j ] 表示在第i行,建造j这个状态的油库需要的代价是多少
num[j]是j这个状态有几个油库(也就是有几个一) 可以直接用 builtinpopcount() b u i l t i n p o p c o u n t ( ) 来算
QAQ然后考虑dp
因为第i行的状态只能转移到i+1的状态转移过来
所以 f[i][j][k] f [ i ] [ j ] [ k ] 表示前i-1行的已经都能够合法,第i行的状态是j,第i-1行的状态时k的最小花费
同时 g[i][j][k] g [ i ] [ j ] [ k ] 则表示对应状态的油库数量
首先我们考虑转移条件~~
因为是要求前i行的状态合法,才能转移到i+1
所以是要求 j|k|p|(j<<1)|(j>>1) j | k | p | ( j << 1 ) | ( j >> 1 ) & (1<<m)−1==(1<<m)−1 ( 1 << m ) − 1 == ( 1 << m ) − 1 之所以这么写,可以yy一下 对~
那么转移的时候,就是优先考虑f数组的大小,然后更新就好
if (f[i][j][k]+sum[i+1][p]<f[i+1][p][j])
{
f[i+1][p][j]=f[i][j][k]+sum[i+1][p];
g[i+1][p][j]=g[i][j][k]+num[p];
}
else
{
if (f[i][j][k]+sum[i+1][p]==f[i+1][p][j])
{
g[i+1][p][j]=min(g[i+1][p][j],g[i][j][k]+num[p]);
}
}
直接给所有的code了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 210;
int a[61][61];
int f[61][maxn][maxn];
int g[61][maxn][maxn];
int sum[61][maxn];
int n,m;
int num[maxn];
int ans1=1e9;
int ans2=1e9;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j]=read();
for (int i=1;i<=n;i++)
{
for (int j=0;j<(1 << m);j++)
{
for (int k=1;k<=m;k++)
{
if (j&(1 << (k-1))) {
sum[i][j]+=a[i][k];
}
}
}
}
memset(f,127/3,sizeof(f));
memset(g,127/3,sizeof(g));
for (int j=0;j<(1 << m);j++) num[j]=__builtin_popcount(j);
for (int i=0;i<(1 << m);i++)
{
f[1][i][0]=sum[1][i];
g[1][i][0]=num[i];
}
for (int i=1;i<=n;i++)
{
for (int j=0;j<(1 << m);j++)
for (int k=0;k<(1 << m);k++)
{
for (int p=0;p<(1 << m);p++)
{
if (((j|k|p|(j<<1)|(j>>1))&((1 << m)-1))==((1 << m)-1))
{
if (f[i][j][k]+sum[i+1][p]<f[i+1][p][j])
{
f[i+1][p][j]=f[i][j][k]+sum[i+1][p];
g[i+1][p][j]=g[i][j][k]+num[p];
}
else
{
if (f[i][j][k]+sum[i+1][p]==f[i+1][p][j])
{
g[i+1][p][j]=min(g[i+1][p][j],g[i][j][k]+num[p]);
}
}
}
}
}
}
for (int i=0;i<(1 << m);i++)
{
if (ans1>f[n+1][0][i])
{
ans1=f[n+1][0][i];
ans2=g[n+1][0][i];
}
else
{
if (ans1==f[n+1][0][i])
ans2=min(ans2,g[n+1][0][i]);
}
}
cout<<ans2<<" "<<ans1;
return 0;
}