可以把一种只有两种状态的一维的数据组用一个整数表示,其二进制的值可以表示每一个位置的状态。如: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);
}