【状压dp练习】洛谷P1896 [SCOI2005]互不侵犯 P2704 [NOI2001]炮兵阵地

可以把一种只有两种状态的一维的数据组用一个整数表示,其二进制的值可以表示每一个位置的状态。如:1010 表示第1、3个位置有,第2、4个位置没有。

洛谷 P1896 [SCOI2005]互不侵犯

init函数枚举所有的m位不存在两个相邻的1的整数,将整数的值记录在status数组中,将此数含有1的数量记录在cnt数组中
dp[i][s][n] 表示进行道第i行时,在第s个状态,使用了n个国王的情况数。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <vector>
typedef std::pair<int ,int> PP;
typedef long long LL;
//const int N = 1e5+5;
LL dp[10][1025][100];
int status[1025],cnt[1025];
int n,k;
int idx;
LL ans;

void init(int v,int b,int total)
{
   if(b >=n){
      status[idx] = v;
      cnt[idx++] = total;
      return ;
   }
   init(v,b+1,total);
   init(v+(1<<b),b+2,total+1);
}

int main()
{
   scanf("%d%d",&n,&k);
   init(0,0,0);
   for(int i=0; i<idx; i++){
      dp[0][i][cnt[i]] += 1;
   }
   for(int r=1; r<n; r++){
      for(int i=0; i<idx; i++){
         for(int j=0; j<idx; j++){
            if(status[i]&status[j] || (status[i]<<1)&status[j] || (status[i]>>1)&status[j]) continue;
            for(int x=cnt[i]; x<=k; x++){
               dp[r][i][x] += dp[r-1][j][x-cnt[i]];
            }
         }
      }
   }
   ans = 0;
   for(int i=0; i<idx; i++) ans += dp[n-1][i][k];
   printf("%lld",ans);
   return 0;
}

P2704 [NOI2001]炮兵阵地

抄完了互不侵犯之后拿这道题练了练手,然后失败。

写一个错误的思路
二维数组dp[i][s]表示进行到第i行时,当状态为s,可以摆放的最多的炮兵的数量。
初始化前两行后,当一种状态s可以摆放,则dp[i][s] = max(自己,dp[i-2][上两行的状态]+上一行状态的总人数+这一行状态的总人数) 即用上到二行为止的最大人数+上一行某状态的人数+这一行s状态的人数来更新这一行的s状态
这种思路存在一个缺陷,即只记录了单行某个状态的最大人数。
沿用这个思路,当进行到第n行时,我们用“截止到n-2行k状态的最大人数”+“n-1行取状态j的人数”+“第n行状态s的人数”来更新第n行。问题在于:我们无法保证当“截止到n-2行k状态的最大人数”取得时,用状态j当作n-2行的下一行时合法的,因为第n-1行的状态受到n-2行和n-3行限制,有可能当“截止到n-2行k状态的最大人数”取得时,j状态是不能与n-3行的状态共存的。然而在这种思路中,我们下意识地认为这种错误时合法的,所以在多数情况下无法得到正确答案。
更正思路:
开三维数组dp[i][s][last],表示进行到第i行时,当状态为s,并且上一行状态为last时,可以摆放的最多的炮兵的数量。
错误代码:(竟然还能得20分)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <vector>
//typedef std::pair<int ,int> PP;
typedef long long LL;
const int N = 105,M = 12,ER9 = 1030;
int rows[N],status[ER9],va[ER9];
LL dp[N][ER9];
char ss[12];
int n,m;
int idx;

int id[N],mp[N][ER9];

int toStatus()
{
   int re = 0;
   for(int i=0; i<m; i++){
      re += (ss[i] == 'H') ? (1<<i) : 0;
   }
   return re;
}

void init(int val,int pos,int cnt1)
{
   if(pos >= m){
      status[idx] = val;
      va[idx++] = cnt1;
      return ;
   }
   init(val,pos+1,cnt1);
   init(val+(1<<pos),pos+3,cnt1+1);
}

int main()
{
   scanf("%d%d",&n,&m);
   for(int i=0; i<n; i++){
      scanf("%s",ss);
      rows[i] = toStatus();
   }
   init(0,0,0);
   for(int i=0; i<idx; i++){
      if(status[i]&rows[0]) continue;
      mp[0][id[0]] = i;
      dp[0][id[0]] = va[i];
      id[0] += 1;
   }
   for(int i=0; i<idx; i++){
      if(status[i]&rows[1]) continue;
      mp[1][id[1]] = i;
      for(int j=0; j<id[0]; j++){
         if(status[i]&status[mp[0][j]]) continue;
         dp[1][id[1]] = std::max(dp[1][id[1]],va[i]+dp[0][j]);
      }
      id[1] += 1;
   }
   for(int r=2; r<n; r++){
      for(int i=0; i<idx; i++){
         if(status[i]&rows[r]) continue;
         mp[r][id[r]] = i;
         for(int j=0; j<id[r-1]; j++){
            for(int k=0; k<id[r-2]; k++){
               if(status[i]&status[mp[r-1][j]] || status[i]&status[mp[r-2][k]] || status[mp[r-2][k]]&status[mp[r-1][j]]) continue;
               dp[r][id[r]] = std::max(dp[r][id[r]],va[i]+va[mp[r-1][j]]+dp[r-2][k]);
            }
         }
         id[r] += 1;
      }
   }
   LL ans = 0;
   for(int i=0; i<id[n-1]; i++){
      ans = std::max(ans,dp[n-1][i]);
   }
   printf("%lld",ans);

   return 0;
}

AC代码:
看很多题解都说要用到三行的滚动数组,否则会MLE。
但是这个没有用,直接开了100行,可能是由于动态加状态节省了空间吧。破案了,题目改过,不限制空间了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <vector>
//typedef std::pair<int ,int> PP;
typedef long long LL;
const int N = 105,M = 12,E10 = 1024;
int rows[N],status[E10],va[E10],dp[N][E10][E10];
//rows:把地图的每一行转化为数字,H为1,P为0,储存在rows数组
//status记录所有状态的对应的数字
//va记录所有状态对应的人数
//dp[i][j][k]表示第i行状态为j,且i-1行状态为k时,可以放置的最大炮兵数
char ss[12];
int n,m;
int idx,ans;
//idx记录status数组的大小
int id[N],mp[N][E10];
//id[i]数组记录第i行可行的总状态数
//mp[i][j]记录第i行第j个状态在status数组中对应的编号


int toStatus()
{
   int re = 0;
   for(int i=0; i<m; i++){
      re += (ss[i] == 'H') ? (1<<i) : 0;
   }
   return re;
}

void init(int val,int pos,int cnt1)
{
   if(pos >= m){
      status[idx] = val;
      va[idx++] = cnt1;
      return ;
   }
   init(val,pos+1,cnt1);
   init(val+(1<<pos),pos+3,cnt1+1);
}

int main()
{
   scanf("%d%d",&n,&m);
   for(int i=0; i<n; i++){
      scanf("%s",ss);
      rows[i] = toStatus();
   }
   init(0,0,0);
   for(int i=0; i<idx; i++){
      if(status[i]&rows[0]) continue;
      mp[0][id[0]] = i;
      dp[0][id[0]][0] = va[i];
      id[0] += 1;
   }
   for(int i=0; i<idx; i++){
      if(status[i]&rows[1]) continue;
      mp[1][id[1]] = i;
      for(int j=0; j<id[0]; j++){
         if(status[i]&status[mp[0][j]]) continue;
         dp[1][id[1]][j] = va[i] + dp[0][j][0];
      }
      id[1] += 1;
   }
   for(int r=2; r<n; r++){
      for(int i=0; i<idx; i++){
         if(status[i]&rows[r]) continue;
         mp[r][id[r]] = i;
         for(int j=0; j<id[r-1]; j++){
            for(int k=0; k<id[r-2]; k++){
               if(status[i]&status[mp[r-1][j]] || status[i]&status[mp[r-2][k]] || status[mp[r-2][k]]&status[mp[r-1][j]]) continue;
               dp[r][id[r]][j] = std::max(dp[r][id[r]][j],va[i]+dp[r-1][j][k]);
            }
         }
         id[r] += 1;
      }
   }
   for(int i=0; i<id[n-1]; i++) for(int j=0; j<id[n-2]; j++){
      ans = std::max(dp[n-1][i][j],ans);
   }
   printf("%d",ans);
   return 0;
}

换了一波滚动数组,但是还是有10.+MB

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <vector>
//typedef std::pair<int ,int> PP;
typedef long long LL;
const int N = 105,M = 12,E10 = 1024;
int rows[N],status[E10],va[E10],dp[3][E10][E10];
//rows:把地图的每一行转化为数字,H为1,P为0,储存在rows数组
//status记录所有状态的对应的数字
//va记录所有状态对应的人数
//dp[i][j][k]表示第i行状态为j,且i-1行状态为k时,可以放置的最大炮兵数
char ss[12];
int n,m;
int idx,ans;
//idx记录status数组的大小
int id[3],mp[3][E10];
//id[i]数组记录第i行可行的总状态数
//mp[i][j]记录第i行第j个状态在status数组中对应的编号


int toStatus()
{
   int re = 0;
   for(int i=0; i<m; i++){
      re += (ss[i] == 'H') ? (1<<i) : 0;
   }
   return re;
}

void init(int val,int pos,int cnt1)
{
   if(pos >= m){
      status[idx] = val;
      va[idx++] = cnt1;
      return ;
   }
   init(val,pos+1,cnt1);
   init(val+(1<<pos),pos+3,cnt1+1);
}

int main()
{
   scanf("%d%d",&n,&m);
   for(int i=0; i<n; i++){
      scanf("%s",ss);
      rows[i] = toStatus();
   }
   init(0,0,0);
   for(int i=0; i<idx; i++){
      if(status[i]&rows[0]) continue;
      mp[0][id[0]] = i;
      dp[0][id[0]][0] = va[i];
      id[0] += 1;
   }
   for(int i=0; i<idx; i++){
      if(status[i]&rows[1]) continue;
      mp[1][id[1]] = i;
      for(int j=0; j<id[0]; j++){
         if(status[i]&status[mp[0][j]]) continue;
         dp[1][id[1]][j] = va[i] + dp[0][j][0];
      }
      id[1] += 1;
   }
   for(int r=2; r<n; r++){
      int rm3 = r%3,rs1m3 = (r-1)%3, rs2m3 = (r-2)%3;
      memset(dp[rm3],0,sizeof(rm3));
      id[rm3] = 0;
      for(int i=0; i<idx; i++){
         if(status[i]&rows[r]) continue;
         mp[rm3][id[rm3]] = i;
         for(int j=0; j<id[rs1m3]; j++){
            for(int k=0; k<id[rs2m3]; k++){
               if(status[i]&status[mp[rs1m3][j]] || status[i]&status[mp[rs2m3][k]] || status[mp[rs2m3][k]]&status[mp[rs1m3][j]]) continue;
               dp[rm3][id[rm3]][j] = std::max(dp[rm3][id[rm3]][j],va[i]+dp[rs1m3][j][k]);
            }
         }
         id[rm3] += 1;
      }
   }
   int ns1m3 = (n-1)%3, ns2m3 = (n-2)%3;
   for(int i=0; i<id[ns1m3]; i++) for(int j=0; j<id[ns2m3]; j++){
      ans = std::max(dp[ns1m3][i][j],ans);
   }
   printf("%d",ans);
   return 0;
}

2020ICPC 江西省大学生程序设计竞赛L WZB’s Harem

题目链接https://ac.nowcoder.com/acm/contest/15806/L
题目翻译: n皇后问题的退化版——n车问题 n<=20
状压 yyds

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <set>
typedef long long int LL;
const int INF = 0x7fffffff, SCF = 0x3fffffff;
const int N = 21, MOD =  1000000007;
int status[N][1<<N],dp[N][1<<N];
int id[N];
int board[N];
int qu[N];
int n;
int toStatus()
{
    int re = 0;
    for(int i=1; i<=n; i++){
        re = (re<<1)+qu[i];
    }
    return re;
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++){
            scanf("%d",&qu[j]);
        }
        board[i] = toStatus();
    }
    for(int i=0; i<(1<<n); i++){
        int tt = i, cnt = 0;
        while(tt > 0){
            cnt += (tt&1);
            tt >>= 1;
        }
        status[cnt][id[cnt]++] = i;
    }
    dp[0][0] = 1; //No.Status 0, id 0;
    for(int i=1; i<=n; i++){
        for(int k=0; k<id[i-1]; k++){
            int pre = status[i-1][k];
            for(int j=0; j<n; j++){
                if((pre>>j)&1) continue;
                if( !( (1<<j) & board[i]) ) {
                    int nxt = pre | (1<<j);
                    dp[i][nxt] = (dp[i][nxt] + dp[i-1][pre]) % MOD;
                }
            }
        }
    }
    LL ans = 1;
    for(int i=2; i<=n; i++){
        ans = ans*i%MOD;
    }
    printf("%lld",ans * dp[n][(1<<n)-1] % MOD);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的公寓报修管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本公寓报修管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此公寓报修管理系统利用当下成熟完善的Spring Boot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。公寓报修管理系统有管理员,住户,维修人员。管理员可以管理住户信息和维修人员信息,可以审核维修人员的请假信息,住户可以申请维修,可以对维修结果评价,维修人员负责住户提交的维修信息,也可以请假。公寓报修管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:公寓报修管理系统;Spring Boot框架;MySQL;自动化;VUE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值