区间dp专题练习
区间dp专题练习
1.Equal Sum Partitions
? 这嘛东西,\(n^2\)自己写去
\]
\]
2.You Are the One
感觉自己智力被吊打
\(dp[i][j]\)表示 , 对于当前的一个空栈 , \(i\)到\(j\)这一段都出栈的最小花费
显然是长得一副区间(诡)dp(异)的样子 , 如何转移呢?(建议自己想想吧)
对于一个\(dp[i][j]\),因为这个\(i\)必须是最先入栈的 , 所以我们可以枚举它的出栈时间\(k\) , 那么总贡献就是
\]
即先出栈的\(i+1...k\)的贡献和+i自己的贡献+\(k+1..j\)这一段的贡献和它们被推迟\(k-i+1\)位产生的贡献
rep(i,1,n) rep(j,i+1,n) dp[i][j]=1e9;
rep(i,1,n) dp[i][i]=a[i];
drep(i,n,1)
rep(j,i,n)
rep(k,i,j)
dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]+(k-i)*a[i]+(k-i+1)*(s[j]-s[k]));
\]
\]
3.Groups
where is my head?
每个人的决策其实就是把一群人分成一组,所以可以直接一个个区间接上去
细节:满足条件的人大于\(R-L+1\)时要取\(min\)
区间dp?
rep(i,1,n) a[i]=rd(),b[i]=rd();
rep(i,0,n) rep(j,0,n) c[i][j]=0;
rep(i,0,n) dp[i]=0;
rep(i,1,n) c[a[i]][n-b[i]]++;
rep(i,0,n-1) rep(j,i+1,n) dp[j]=max(dp[j],dp[i]+min(j-i,c[i][j]));
printf("%d\n",dp[n]);
4.Sky Soldiers
又是假区间dp
const int N=1010;
const double eps=1e-6;
int n,m;
map <int,double> M;
typedef map<int,double> ::iterator iter;
int p[N];
double x[N];
int cnt;
double dp[N][51];//前i个,放j个站
double sum[N][N];
int main(){
while(~scanf("%d%d",&n,&m)&&n&&m) {
M.clear();
rep(i,1,n) {
rep(j,1,rd()) {
int p=rd();
double x;scanf("%lf",&x);
M[p]+=x;
}
}
cnt=0;
for(iter it=M.begin();it!=M.end();++it) {
p[++cnt]=it->first;
x[cnt]=it->second;
}//暴力离散
double ans=1e11;
rep(i,0,cnt) rep(j,0,m) dp[i][j]=1e11;
rep(i,1,cnt) rep(j,1,cnt) sum[i][j]=sum[i][j-1]+x[j]*abs(p[i]-p[j]);//预处理便于算贡献
rep(i,1,cnt) dp[i][1]=sum[i][i];
rep(i,1,cnt) {
rep(j,1,m-1) if(dp[i][j]<1e10) {
rep(k,i+1,cnt) {
int mid=(p[i]+p[k])>>1;
mid=lower_bound(p+1,p+cnt+1,mid)-p;
double s=min(sum[i][mid]-sum[i][i]+sum[k][k]-sum[k][mid],sum[i][mid-1]-sum[i][i]+sum[k][k]-sum[k][mid-1]);//贡献
dp[k][j+1]=min(dp[k][j+1],dp[i][j]+s);
}
}
}
rep(i,1,cnt) ans=min(ans,dp[i][m]+sum[i][cnt]-sum[i][i]);
printf("%.2lf\n",ans);
}
}
\]
\]
5.Play Game
假区间dp again
\(dp[l1][r1][l2][r2]\)表示两个序列分别还剩下\(l1,r1\),\(l2,r2\)的答案 , 很基础但是有细节
int n,m;
int a[N],b[N];
int dp[N][N][N][N];
int sum1[N],sum2[N];
int main(){
rep(kase,1,rd()) {
n=rd();
rep(i,1,n) sum1[i]=a[i]=rd(),sum1[i]+=sum1[i-1];
rep(i,1,n) sum2[i]=b[i]=rd(),sum2[i]+=sum2[i-1];
memset(dp,0,sizeof dp);
drep(l1,n+1,1) rep(r1,l1-1,n) drep(l2,n+1,1) rep(r2,l2-1,n) {
if(l1>r1&&l2>r2) continue;
int s=1e9;
if(l1<=r1) s=min(s,min(dp[l1+1][r1][l2][r2],dp[l1][r1-1][l2][r2]));
if(l2<=r2) s=min(s,min(dp[l1][r1][l2+1][r2],dp[l1][r1][l2][r2-1]));
dp[l1][r1][l2][r2]=sum1[r1]-sum1[l1-1]+sum2[r2]-sum2[l2-1]-s;
}
printf("%d\n",dp[1][n][1][n]);
}
}
\]
\]
6.Two Rabbits
这个鬼题是真的惊了
首先肯定要接两倍数组断成链
然后我们分析题目性质,对于任意路径,互为重合逆子序列时最优
即跳跃路径为原序列的一个回文子序列时一定更优
若两只兔子的路径错开,那么一定可以向两边继续延伸
细节:一个点时回文长度为1,当跳跃总长小于n时答案要加一
int n,m;
int a[N];
int dp[N][N];
int main(){
srand(618);
while(~scanf("%d",&n)&&n){
rep(i,1,n) a[i]=a[i+n]=rd();
int ans=0;
memset(dp,0,sizeof dp);
rep(l,1,n) drep(i,n*2-l+1,1) {
int j=i+l-1;
if(a[i]==a[j]) dp[i][j]=dp[i+1][j-1]+2-(i==j);
else dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
ans=max(ans,dp[i][j]+(l<n));
}
printf("%d\n",ans);
}
}
\]
\]
7.Dire Wolf
这个题长得还是比较区间dp的
表示自己的转移错得离谱都过掉了。。。
正确的转移方法和T2有点类似,即枚举最后一个杀死的狼,加上前后直接转移
int n,m;
int a[N],b[N];
int dp[N][N];
int main(){
rep(kase,1,rd()){
rep(i,1,n=rd()) a[i]=rd();
rep(i,1,n) b[i]=rd();b[n+1]=a[n+1]=0;
//memset(dp,10,sizeof dp);
drep(i,n,1) rep(j,i,n) {
dp[i][j]=1e9;
if(i==j) dp[i][j]=a[i]+b[i-1]+b[i+1];
else rep(k,i,j) dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+a[k]+b[i-1]+b[j+1]);
//第k只狼最后杀死,贡献分开算
}
printf("Case #%d: %d\n",kase,dp[1][n]);
}
}
\]
\]
8.Dylans loves sequence
这个题令我不想说话
int n,m;
int a[N],b[N],cnt;
int s[N],ans[N][N];
void Add(int p,int x){
while(p<=n) s[p]+=x,p+=p&-p;
}
int Que(int p){
int res=0;
while(p) res+=s[p],p-=p&-p;
return res;
}
int main(){
n=rd(),m=rd();
rep(i,1,n) a[i]=b[i]=rd();
sort(b+1,b+n+1),cnt=unique(b+1,b+n+1)-b-1;
rep(i,1,n) a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
rep(l,1,n) {
memset(s,0,sizeof s);
rep(r,l,n) {
ans[l][r]=ans[l][r-1]+r-l-Que(a[r]);
Add(a[r],1);
}
}
rep(i,1,m) {
int x=rd(),y=rd();
printf("%d\n",ans[x][y]);
}
}
9.Expression
首先要理解题意
所以符号是任意安排顺序,每个符号相当于合并两个数
所以总方案数应该是\((n-1)!\)
转移是也要注意,应该乘上左右两个区间符号交叉出现的价值
将区间\([i,k];[k+1,j]\)合并至\([i,j]\)时要乘上的贡献是
\]
(因为固定了第\(k\)个符号最后,所以是\(j-i-1\))
int n,m;
int a[N],opt[N];
ll g[N][N];
ll fac[N]={1},Inv[N]={1,1};
int main(){
rep(i,1,N-1) fac[i]=fac[i-1]*i%P;
rep(i,2,N-1) Inv[i]=1ll*(P-P/i)*Inv[P%i]%P;
rep(i,1,N-1) Inv[i]=Inv[i]*Inv[i-1]%P;
while(~scanf("%d",&n)) {
rep(i,1,n) a[i]=rd();
rep(i,1,n-1) {
char ch;
do ch=getchar();
while(ch!='+'&&ch!='-'&&ch!='*');
if(ch=='+') opt[i]=1;
else if(ch=='-') opt[i]=2;
else opt[i]=3;
}
memset(g,0,sizeof g);
drep(i,n,1) rep(j,i,n) {
if(i==j) g[i][i]=a[i];//边界条件
rep(k,i,j-1) {
ll t=Inv[k-i]%P*Inv[j-k-1]%P*fac[j-i-1]%P;
if(opt[k]==1) {
g[i][j]=(g[i][j]+(fac[k-i]*g[k+1][j]%P+fac[j-k-1]*g[i][k]%P)*t)%P;
} else if(opt[k]==2) {
g[i][j]=(g[i][j]+(-1ll*fac[k-i]*g[k+1][j]%P+1ll*fac[j-k-1]*g[i][k]%P)*t)%P;
} else {
g[i][j]=(g[i][j]+g[i][k]*g[k+1][j]%P*t)%P;
}
}
}
printf("%lld\n",(g[1][n]%P+P)%P);//可能有负数
}
}
\]
\]
10.QSC and Master
众所周知我是一个擅长乱搞的人,所以这个奇怪的题我也是乱搞搞过去的
它长得并不是一个标准的区间dp
两种决策:
直接从左右端点向两边扩展 ;
将这段区间切开
注意由于求得是整个序列的答案所以最后还要合并一下
int n,m;
int a[N],b[N];
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }
ll dp[N][N];
int main(){
rep(kase,1,rd()){
n=rd();
rep(i,1,n) a[i]=rd();
rep(i,1,n) b[i]=rd();
memset(dp,-63,sizeof dp);
drep(i,n,1) rep(j,i+1,n) {
if(j-i==1) {
if(gcd(a[i],a[j])!=1) dp[i][j]=b[i]+b[j];
continue;
}
if(gcd(a[i],a[j])!=1) dp[i][j]=max(dp[i][j],dp[i+1][j-1]+b[i]+b[j]);//决策一
rep(k,i,j) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);//决策二
}
ll ans=0;
rep(i,1,n) {
dp[1][i]=max(dp[1][i],0ll);
rep(j,1,i) dp[1][i]=max(dp[1][i],max(0ll,dp[1][j])+max(0ll,dp[j+1][i]));
}//合并答案
rep(i,1,n) rep(j,i,n) ans=max(ans,dp[i][j]);
printf("%lld\n",ans);
}
}
\]
\]
11.Kirinriki
跑错的尺取
const int N=5100,P=1e9+7;
int n,m;
char s[N];
int dp[N];
int main(){
rep(kase,1,rd()){
m=rd();
scanf("%s",s+1);n=strlen(s+1);
int ans=0;
rep(i,3,n*2-1) {
reg int cnt=0,p=1;
drep(j,i/2-(!(i&1)),1) {
if(i-j>n) break;
cnt++;
dp[cnt]=dp[cnt-1]+abs(s[j]-s[i-j]);
while(dp[cnt]-dp[p-1]>m) p++;
ans=max(ans,cnt-p+1);
}
}
printf("%d\n",ans);
}
}
\]
\]
12.Zuma
这个题没有素质!它卡常!
\(dp[i][j][0...4]\)表示
0:消完了
1:还剩1个0
2:还剩2个0
3:还剩1个1
4:还剩2个1
转移极其繁琐
卡常技巧:相邻相同的可以压成一个块
(希望我没有想错吧)
const int N=410;
#define chk(a,b) (a>b&&(a=b))//卡常
int n,m;
char s[N];
int a[N],b[N],c[N],dp[N][N][5];
//0 --> 0
//1 --> one 0
//2 --> two 0
//3 --> one 1
//4 --> two 1
int main(){
int T; scanf("%d",&T);
rep(kase,1,T){
scanf("%s",s+1);
n=strlen(s+1);
rep(i,1,n) a[i]=s[i]^'0';
int _n=0;
rep(i,1,n) if(i==1||a[i]!=a[i-1]) {
_n++;
b[_n]=1; c[_n]=a[i];
} else b[_n]++,b[_n]=b[_n];//块合并
n=_n;
rep(i,1,n) rep(j,1,n) rep(o,0,4) dp[i][j][o]=1e9;
rep(i,1,n) {
dp[i][i][c[i]*2+b[i]]=0;
dp[i][i][0]=3-b[i];
}//边界条件初始化
drep(i,n,1) rep(j,i+1,n) {
rep(k,i,j-1){
reg int *x=dp[i][k],*y=dp[k+1][j];//指针卡常
chk(dp[i][j][0],x[0]+y[0]);
chk(dp[i][j][0],x[1]+y[2]);
chk(dp[i][j][0],x[2]+y[2]);
chk(dp[i][j][0],x[2]+y[1]);
chk(dp[i][j][0],x[3]+y[4]);
chk(dp[i][j][0],x[4]+y[3]);
chk(dp[i][j][0],x[4]+y[4]);
chk(dp[i][j][1],x[0]+y[1]);
chk(dp[i][j][1],x[1]+y[0]);
chk(dp[i][j][2],x[1]+y[1]);
chk(dp[i][j][2],x[0]+y[2]);
chk(dp[i][j][2],x[2]+y[0]);
chk(dp[i][j][3],x[0]+y[3]);
chk(dp[i][j][3],x[3]+y[0]);
chk(dp[i][j][4],x[3]+y[3]);
chk(dp[i][j][4],x[0]+y[4]);
chk(dp[i][j][4],x[4]+y[0]);
}
chk(dp[i][j][0],dp[i][j][1]+2);
chk(dp[i][j][0],dp[i][j][2]+1);
chk(dp[i][j][0],dp[i][j][3]+2);
chk(dp[i][j][0],dp[i][j][4]+1);
}
printf("Case #%d: %d\n",kase,dp[1][n][0]);
}
}
(题解写到一半突然dalao来质问我,发现这种做法好像是错的。。。,但还不清楚是不是)
如果发现有错误请写评论
区间dp专题练习的更多相关文章
- 区间dp专题
HDU4283You Are the One区间dp, 记忆话搜索运行时间: #include <iostream> #include <cstdio> #include ...
- kuangbin专题十二 POJ3186 Treats for the Cows (区间dp)
Treats for the Cows Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7949 Accepted: 42 ...
- 「kuangbin带你飞」专题二十二 区间DP
layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...
- [kuangbin带你飞]专题二十二 区间DP
ID Origin Title 17 / 60 Problem A ZOJ 3537 Cake 54 / 105 Problem B LightOJ 1422 Hallowee ...
- 专题训练之区间DP
例题:以下例题部分的内容来自https://blog.csdn.net/my_sunshine26/article/details/77141398 一.石子合并问题 1.(NYOJ737)http: ...
- UESTC 2015dp专题 A 男神的礼物 区间dp
男神的礼物 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/65 Descri ...
- 【专题】区间dp
1.[nyoj737]石子合并 传送门:点击打开链接 描述 有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这 ...
- P1040 加分二叉树 区间dp
题目描述 设一个nn个节点的二叉树tree的中序遍历为(1,2,3,…,n1,2,3,…,n),其中数字1,2,3,…,n1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第ii个节 ...
- dp专题训练
****************************************************************************************** 动态规划 专题训练 ...
随机推荐
- 【前端_js】Bootstrap之表单验证
Bootstrap表单验证插件bootstrapValidator使用方法整理 BootstrapValidator 表单验证超详细教程
- 解决Android studio导航tools下没有android怎么打开DDMS工具
因为这个功能用的人少, 新版本就去掉菜单入口了, 但这个功能并没有删除,打开方式:点开terminal ,也就是AS自带的命令行工具, 输入monitor 回车就启动了, 1.点开terminal 2 ...
- 分析一个UBOOT的方法
1. 编译完成后可以看到在主目录下生成了uboot.bin文件,为了方便分析,使用如下命令将其反汇编:arm-linux-objdump -D -m arm u-boot > u-boot.as ...
- python匹配linux通配符
有时候需要匹配linux中的通配符,例如*和?,它们的含义为: *:匹配0个或多个字符: ?:匹配任意单个字符. 这和正则表达式中含义不一样,在正则表达式中: *:匹配前一个字符0次或者多次: ?:匹 ...
- Odoo启动运行参数(script运行参数,不是运行配置文件)
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826315.html 一:启动选项用在哪里 如果你是用Pycharm进行odoo二次开发的话,可以通过 R ...
- 202. 快乐数.Set去重作用实际作用
编写一个算法来判断一个数是不是“快乐数”. 一个“快乐数”定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1.如 ...
- saltstack--状态判断unless与onlyif
saltstack状态判断unless与onlyif 很多时候我们在编写 state 文件时候需要进行判断,判断该目录或文件是否存在,判断该配置是否已经已添加,然后根据判断结果再决定命令或动作是否执行 ...
- (六)Kubernetes Pod控制器-ReplicaSet和Deployment和DaemonSet
Pod控制器相关知识 控制器的必要性 自主式Pod对象由调度器调度到目标工作节点后即由相应节点上的kubelet负责监控其容器的存活状态,容器主进程崩溃后,kubelet能够自动重启相应的容器.但对出 ...
- Apache JMeter系列.1
最爱看统计 --01-- 简介 Apache JMeter Apache JMeter可用于测试静态和动态资源(文件,Servlet,Perl脚本,Java对象,数据库和查询,FTP服务器等)的性能. ...
- 图论 - PAT甲级 1003 Emergency C++
PAT甲级 1003 Emergency C++ As an emergency rescue team leader of a city, you are given a special map o ...