拯救莫莉斯
一道考试题:然而谁会想到我考试的时候打了一个最小费用最大流??
真是弱爆了。。。
拯救莫莉斯
时间限制: 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 }