首页 > 其他分享 >Living-Dream 系列笔记 第55期

Living-Dream 系列笔记 第55期

时间:2024-04-29 22:12:38浏览次数:26  
标签:Living 55 long times int Dream 列会 dp MOD

状压 dp 空间优化技巧:

  • 滚动数组

  • 提前预处理出有效状态

T1

典题限时返场。

上次讲的时候的代码用到了滚动数组,这次讲第二种优化。

其实很简单,就是在 dp 前将所有状态枚举一遍,将同行冲突的判掉,合法的用 \(st_i\) 存储即可。

方法很 bf,但经试验可以发现一千多状态中仅有几十个合法的,因此效率能快得飞起。

哦对了矩阵上的状压一般初始状态都在第 \(0\) 行。

code
#include<bits/stdc++.h>
#define int long long 
using namespace std;

int n,m;
int soil[105],val[1<<15];
int dp[105][205][205];
int tot,st[205];

signed main(){
	cin>>n>>m;
	memset(dp,0xcf,sizeof(dp));
	dp[0][1][1]=0;
	char c;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>c,soil[i]=(soil[i]<<1)+(c=='P');
	for(int i=0;i<(1<<m);i++){
		if((i&(i<<1))||(i&(i<<2))) continue;
		st[++tot]=i,val[i]=__builtin_popcount(i);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=tot;j++){
			if((soil[i]&st[j])!=st[j]) continue;
			for(int p=1;p<=tot;p++){
				if(st[j]&st[p]) continue;
				for(int q=1;q<=tot;q++){
					if(st[j]&st[q]) continue;
					dp[i][j][p]=max(dp[i][j][p],dp[i-1][p][q]+val[st[j]]);
				}
			}
		}
	}
	int ans=-1e9;
	for(int i=1;i<=tot;i++)
		for(int j=1;j<=tot;j++)
			ans=max(ans,dp[n][i][j]);
	cout<<ans;
	return 0;
}

T2 / T3

这两题一模一样且都很典,所以放到一起讲了。

其实就是 T1 多加一个维度,即前 \(i\) 行共放了 \(x\) 个国王。

然后在循环中枚举 \(x\),有转移:

\[dp_{i,j,x}=dp_{i,j,x}+dp_{i-1,k,x-c} \]

其中 \(i\) 表示当前行,\(j,k\) 表示当前行与上一行的状态,\(c\) 表示当前行放了几个国王(即 \(j\) 中 \(1\) 的个数)

然后改一下判定合法的方式即可,其他的和 T1 完全一致。

这是 T3 的做法,至于 T2 也是仅需改一下判定合法的方式,然后多加一个维度(因为有上两行)并状压列即可。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=10,K=1e2+5;
int n,k;
int dp[N][1<<N][K];

signed main(){
	cin>>n>>k;
	dp[0][0][0]=1;
	for(int i=1;i<=n;i++){
		for(int kk=0;kk<=k;kk++){
			for(int j=0;j<(1<<n);j++){
				int c=__builtin_popcount(j);
				if(j&(j<<1)||c>kk) continue;
				for(int l=0;l<(1<<n);l++){
					if((j&l)||((j<<1)&l)||((j>>1)&l)||((l<<1)&l)) continue;
					dp[i][j][kk]+=dp[i-1][l][kk-c];
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<(1<<n);i++) ans+=dp[n][i][k];
	cout<<ans;
	return 0;
}

作业 T1

曾经用 bf + 剪枝过的,很奇特。

但是这题状压更简单,仅需在设初始状态时将每个点的初始 dp 值设为与 \((0,0)\) 的距离即可,然后就变成 这里边的 T1 了。

注意初始化 dp 数组要 memset(dp,127,sizeof(dp)),因为 dp 是 double 类型数组。

code
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;

const int N=16;
int n,vis;
double dp[1<<N][N];
pair<double,double> a[N];

int main(){
	cin>>n;
	memset(dp,127,sizeof(dp));
	for(int i=0;i<n;i++)
		cin>>a[i].x>>a[i].y;
	for(int i=0;i<n;i++)
		dp[1<<i][i]=sqrt(a[i].x*a[i].x+a[i].y*a[i].y);
	for(int i=0;i<(1<<n);i++)
		for(int j=0;j<n;j++)
			if((i>>j)&1)
				for(int k=0;k<n;k++)
					if((i>>k)&1)
						dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+sqrt((a[j].x-a[k].x)*(a[j].x-a[k].x)+(a[j].y-a[k].y)*(a[j].y-a[k].y)));
	double ans=1e9;
	for(int i=0;i<n;i++)
		ans=min(ans,dp[(1<<n)-1][i]);
	cout<<setprecision(2)<<fixed<<ans;
	return 0;
}

作业 T2

很有趣味的题,可惜正解不是状压。

题外话:here 是此题的加强版题解,先 mark 下以后研究。

首先很容易得到一个性质:每行、每列的炮的数量 \(\le 2\)

依据这一点,我们想到对于每一行都会有三种列:

没有炮的列、有一个炮的列、有两个炮的列。

于是我们考虑设计如下状态:

令 \(dp_{i,j,k}\) 表示第 \(i\) 行中,\(j\) 列有一个炮,\(k\) 列有两个炮。

显然,空列数 \(= m-j-k\)

然后我们对于第 \(i\) 行,分三种情形进行讨论:

  • 一个都不放

直接 \(dp_{i,j,k}=dp_{i-1,j,k}\)。

  • 放一个

我们可以选择放在空列中,

此时有一个炮的列会增加一个(\(j-1 \to j\)),

于是此时有

\[dp_{i,j,k}=dp_{i,j,k}+dp_{i-1,j-1,k} \times (m-(j-1)-k) \]

同样的,我们也可以选择放在有一个炮的列中,

此时有一个炮的列会减少一个(\(j+1 \to j\)),有两个炮的列会增加一个(\(k-1 \to k\))。

于是此时有

\[dp_{i,j,k}=dp_{i,j,k}+dp_{i-1,j+1,k-1} \times (j+1) \]

  • 放两个

我们可以选择都放在空列中,

此时有一个炮的列会增加两个(\(j-2 \to j\)),

于是此时有

\[dp_{i,j,k}=dp_{i,j,k}+dp_{i-1,j-2,k} \times C^{2}_{m-(j-2)-k} \]

我们也可以选择一个放在空列,一个放在有一个炮的列中,

此时有一个炮的列不变,有两个炮的列会增加一个(\(k-1 \to k\)),

于是此时有

\[dp_{i,j,k}=dp_{i,j,k}+dp_{i-1,j,k-1} \times j \times (m-j-(k-1)) \]

我们还可以选择两个都放在有一个炮的列中,

此时有一个炮的列会减少两个(\(j+2 \to j\)),有两个炮的列会增加两个(\(k-2 \to k\)),

于是此时有

\[dp_{i,j,k}=dp_{i,j,k}+dp_{i-1,j+2,k-2} \times C^{2}_{j+2} \]

其中因为只涉及到 \(m=2\) 的组合数,所以可以 \(O(1)\) 求得,具体见 code。

完结撒花!~

code
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=131,MOD=9999973;
int n,m;
int dp[N][N][N];

int C(int x){
	return x*(x-1)/2;
}

signed main(){
	cin>>n>>m;
	dp[0][0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			for(int k=0;k<=m-j;k++){
				dp[i][j][k]=dp[i-1][j][k];
				if(j>=1) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-1][k]*(m-(j-1)-k))%MOD;
				if(k>=1) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+1][k-1]*(j+1))%MOD;
				if(j>=2) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-2][k]*C(m-(j-2)-k))%MOD;
				if(k>=1) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k-1]*j*(m-j-(k-1)))%MOD;
				if(k>=2) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+2][k-2]*C(j+2))%MOD;
			}
		}
	}
	int ans=0;
	for(int i=0;i<=m;i++)
		for(int j=0;j<=m;j++)
			ans=(ans+dp[n][i][j])%MOD;
	cout<<ans;
	return 0;
}

标签:Living,55,long,times,int,Dream,列会,dp,MOD
From: https://www.cnblogs.com/XOF-0-0/p/18166741

相关文章

  • 洛谷P5597 复读
    洛谷P5597复读首先概括一下题意:文中给出三个指令L(命令机器人向当前节点的左子节点走)、R(命令机器人向当前节点的右子节点走)、U(命令机器人向当前节点的父亲节点走)以及一颗二叉树。初始状态,机器人在二叉树的根节点。当机器人处于二叉树的根节点时,不可以执行指令U,但是机器人可以走......
  • SP1557 GSS2 - Can you answer these queries II
    link题目大意:给一个\(n\)个元素的序列,\(q\)次询问\([l_i,r_i]\)的最大子段和(相同元素只算一个)。\(n,q\le10^5,-10^5\lea_i\le10^5\).解法:首先考虑最大子段和的经典动态解法:维护\(pre_i,suf_i,sum_i,mxsum_i\)。这个时候你会发现无法合并。Tips:对于区间询问问......
  • 实时动态规则(55)规则发布平台后端开发(5) 规则模型开发(4)rulemodel_03_涉及事件时间
    0涉及架构 注意:以下代码,都是根据一个特定规则模型: rulemodel_03_caculator 来进行开发的不同的规则模型,如下功能代码需要进行不同的开发RuleModel_03 这个规则模型的特点是:拥有事件间隔时间1规则参数结构规范{"ruleModelId":"3","ruleId":"m3-r01",......
  • 055、韦讽录事宅观曹将军画马图
    055、韦讽录事宅观曹将军画马图唐●杜甫国初已来画鞍马,神妙独数江都王。将军得名三十载,人间又见真乘黄。曾貌先帝照夜白,龙池十日飞霹雳。内府殷红玛瑙盘,婕妤传诏才人索。盘赐将军拜舞归,轻纨细绮相追飞。贵戚权门得笔迹,始觉屏障生光辉。昔日太宗拳毛䯄,近时郭家狮子花。今......
  • calico配置报错 kubelet.go:2855] "Container runtime network not ready"
    前言配置calico网络插件时,kubectlgetnode报错:NoReadykubectldescribenodenodeName:nodeRoles:<none>Labels:beta.kubernetes.io/arch=amd64beta.kubernetes.io/os=linuxkub......
  • 32天【代码随想录算法训练营34期】第八章 贪心算法 part02 (● 122.买卖股票的最佳时
    122.买卖股票的最佳时机IIclassSolution:defmaxProfit(self,prices:List[int])->int:result=0foriinrange(len(prices)-1):ifprices[i+1]-prices[i]>0:result+=prices[i+1]-prices[i]return......
  • Living-Dream 系列笔记 第54期
    状压dp:是对dp状态表示的优化。若有多个维度,每个维度仅有\(0/1\),则将状态转为一个二进制数,并以十进制数表示。位操作(全文背诵):任意二进制数位\(\operatorname{and}\1\)得本身。任意二进制数位\(\operatorname{xor}\1\)得本身。任意二进制数位\(\o......
  • com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed at S
     适用的druid数据库连接池一直有问题,无法连接,但是什么都没改过。排查了数据库(数据库单独连接没问题)、防火墙、IP白名单等步骤后,重启服务器、重启应用后都无法解决。重启应用过程中发现了应用无法正常启动的情况,这点让人觉得很意外,于是想看下现在服务器上运行的jar包情况,命令是......
  • 31天【代码随想录算法训练营34期】第八章 贪心算法 part01(● 理论基础 ● 455.分发
    贪心算法就是先选局部最优,再推全局最优没有套路将问题分解为若干个子问题找出适合的贪心策略求解每一个子问题的最优解将局部最优解堆叠成全局最优解●455.分发饼干classSolution:deffindContentChildren(self,g:List[int],s:List[int])->int:g.s......
  • CF1955H The Most Reckless Defense
    给敌人加血可以看成是减少防御塔的攻击力,那么一个塔对敌人能造成的最大伤害即为\(500\pir^2-3^r\),注意到\(r=12\)时已经小于\(0\)了,所以半径不会很大,又因为每个\(r\)只能选一次,所以有效的塔很少,考虑状压\(dp\)。具体地,我们设\(f_{i,S}\)表示前\(i\)个塔中,被选到的塔......