【状压DP】[GDOI2014]拯救莫莉斯

    科技2022-08-20  123

    题目

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

    圣域的地图可以看成是一个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; }
    Processed: 0.014, SQL: 9