hihocoder #1044 Q - 状态压缩·一 【第四周练习】【the fourth day】

本文介绍了一个有趣的问题场景:如何在不引发冲突的情况下最大程度地清理车厢内的垃圾。文章详细阐述了解决这一问题的过程,包括理解问题背景、应用动态规划原理、进行状态压缩等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

描述

小Hi和小Ho在兑换到了喜欢的奖品之后,便继续起了他们的美国之行,思来想去,他们决定乘坐火车前往下一座城市——那座城市即将举行美食节!

但是不幸的是,小Hi和小Ho并没有能够买到很好的火车票——他们只能够乘坐最为破旧的火车进行他们的旅程。

不仅如此,因为美食节的吸引,许多人纷纷踏上了和小Hi小Ho一样的旅程,于是有相当多的人遭遇到了和小Hi小Ho一样的情况——这导致这辆车上的人非常非常的多,以至于都没有足够的位置能让每一个人都有地方坐下来。

小Hi和小Ho本着礼让他们的心情——当然还因为本来他们买的就是站票,老老实实的呆在两节车厢的结合处。他们本以为就能够这样安稳抵达目的地,但事与愿违,他们这节车厢的乘务员是一个强迫症,每隔一小会总是要清扫一次卫生,而时值深夜,大家都早已入睡,这种行为总是会惊醒一些人。而一旦相邻的一些乘客被惊醒了大多数的话,就会同乘务员吵起来,弄得大家都睡不好。

将这一切看在眼里的小Hi与小Ho决定利用他们的算法知识,来帮助这个有着强迫症的乘务员——在不与乘客吵起来的前提下尽可能多的清扫垃圾。

小Hi和小Ho所处的车厢可以被抽象成连成一列的N个位置,按顺序分别编号为1..N,每个位置上都有且仅有一名乘客在休息。同时每个位置上都有一些垃圾需要被清理,其中第i个位置的垃圾数量为Wi。乘务员可以选择其中一些位置进行清理,但是值得注意的是,一旦有编号连续的M个位置中有超过Q个的位置都在这一次清理中被选中的话(即这M个位置上的乘客有至少Q+1个被惊醒了),就会发生令人不愉快的口角。而小Hi和小Ho的任务是,计算选择哪些位置进行清理,在不发生口角的情况下,清扫尽可能多的垃圾。

提示一:无论是什么动态规划,都需要一个状态转移方程!

提示二:好像什么不对劲?状态压缩哪里去了?

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第一行为三个正整数N、M和Q,意义如前文所述。

每组测试数据的第二行为N个整数,分别为W1到WN,代表每一个位置上的垃圾数目。

对于100%的数据,满足N<=1000, 2<=M<=10,1<=Q<=M, Wi<=100

输出

对于每组测试数据,输出一个整数Ans,表示在不发生口角的情况下,乘务员最多可以清扫的垃圾数目。

Sample Input
5 2 1
36 9 80 69 85 
Sample Output
201

题意:给定n个各个位置的垃圾数量,要求在连续m个座位中,清洁的座位不超过q个,求能清洁的最多垃圾数。

思路:动态规划思想之一是无后效性,而此题中限制了位置选取的数量,当位于第i个位置时,选or不选,按照一般动态规划思想,需要根据前面已选的座位数列出状态转移方程式,然而我们不可能每种情况都列出,所以,需要用到状态压缩。

  1. 枚举可行状态。将在m个座位数量的限制下,枚举每一种可能的状态,可行状态为2^m-1种(一开始我认为是2^m种,后来发现,我认为的这种情况是将q==m情况考虑了进来,而实际题目是不能够这样的,因为毫无推导意义,直接计算总和就好),并将可行状态保存下来。
  2. 列状态转移方程式。二维数组的行是每个位置的标号,列是每种可行状态,按照座位号从小到大递推。在第i个位置k状态下的最大垃圾清理数量,需要从前一个位置i-1中获得或者说前m-1个位置中获得,所以我们只需要比较出
    dp[第i-1个位置][k状态去掉i位置决策后的状态],dp[第i-1个位置][k状态去掉i位置决策后状态+第i-m+1选定的状态]
     的较大值即可,i-m+1位置处存储第1个决策,i位置处存储第m个决策,因为我们不知道是做了第i个位置的决策后的价值大(注意,决策里包括了清理or不清理),还是第i-m+1个位置清理后的价值大,所以需要进行比较,比如1,2,3,4,5,6,假设 i=6,m=5,在做了第i=6个位置的决策后,位置的状态是2,3,4,5,6,而未做第i=6个位置的决策前,位置的状态是1,2,3,4,5,所以需要将不同位置状态下的价值进行比较。
      
    单独解释为什么第i个位置不一定清理,因为我们只需要从1到i的最大垃圾清理数量,所以第i个位置是否清理并不重要,比如清理了第i个位置的垃圾,而且刚好是决策数刚好达到m,如果i+1位置处的垃圾数远大于i处的垃圾数,而又因为决策数已经达到了最大m无法再清理i+1处的垃圾,这种情况,并不需要清理i处的垃圾

  3. 遍历状态输出结果。在n处是最后的递推位置,由于1-n之间不同状态的价值不同,所以需要比较出最大值。
总结:就怕胆子不够大,开的脑洞不够,说到底还是题写少了
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define N 10010
#define inf 0x3f3f3f3f
int dp[N][1025],vis[1025],ans[1025],value[N];
int n,m,q;
int b[11] = {1,2,4,8,16,32,64,128,256,512,1024};//m进制下最左边位置已选的状态 
int top;

int Judge(int x)
{
	int count = 0;
	while(x)
	{
		if(x%2)
			count ++;
		x = x>>1;
	}
	return count;
}
void Init()
{
	int length;
	top = 0;
	memset(vis,inf,sizeof(vis));
	for(int i = 0; i < (1<<m);i++)
	{
		length = Judge(i);//计算i排列状态下,1的个数总和
		if( length <= q)
			vis[i] = length;//vis存储i状态下1的个数 
	}
	for(int i = 0; i < (1<<m);i++)
	{
		if(vis[i] <= q)
			ans[++top] = i;//在1不超过q的情况下将i状态存储 
	}
	return;
}

int main()
{
	int sum;
	while(scanf("%d%d%d",&n,&m,&q)!=EOF)
	{
		memset(dp,0,sizeof(dp));
		Init();
		for(int i = 1; i <= n; i ++)
			scanf("%d",&value[i]);
		for(int i = 1; i <= n; i ++)//从m位置开始枚举到n位置 
		{
			for(int k = 1; k <= top; k ++)//枚举top个状态下的最大价值 
			{ 
				//找到在第i个位置ans[k]状态下的最大价值 
				dp[i][ans[k]] = max(dp[i-1][ans[k]>>1],dp[i-1][(ans[k]>>1)+b[m-1]])+(ans[k]&1)*value[i];
				//在第i-1个位置处,去掉第i个位置决策的状态的价值(没有加入i-m+1的决策状态) 
				//在第i-1个位置处, 去掉第i个位置决策的状态+第i-m+1个位置为1即清理的状态的价值(加入i-m+1位置已选的状态) 
				//是否清理i位置处,是由ans[k]状态下的末位数决定,因为末位为1时表示清理i位置 
			}
		}
		sum = 0;
		for(int i = 1; i <= top; i ++)
			sum = max(sum,dp[n][ans[i]]);//n是最终位置,由于不同状态价值不同,所以需要取最大值 
		printf("%d\n",sum);//好歹理解完了,万岁 
	}
	return 0;
}


import maya.cmds as cmds windowName = "AssetsLibraryBatev8" if cmds.window(windowName, ex=True): cmds.deleteUI(windowName) cmds.window(windowName) cmds.columnLayout(adj=1) # body---- cmds.rowColumnLayout( numberOfColumns=3, adj=2, cw=[(1, 185), (3, 200)], h=30, bgc=[0.1, 0.1, 0.1] ) # NAV---- cmds.setParent("..") # NAV---- cmds.rowColumnLayout( numberOfColumns=2, adj=2, cw=[(1, 185)], h=690, bgc=[0.3, 0.3, 0.3] ) # workspace---- cmds.columnLayout(adj=1, h=690, bgc=[0.15, 0.15, 0.15]) # left---- cmds.setParent("..") # left---- # flip--- form = cmds.formLayout(nd=100) flow_layout = cmds.flowLayout(cs=20, bgc=[0.3, 0.3, 0.3], w=600, h=50) def button_ui(Normal_color): def button_change(*args): for buffer in all_button: bgc = cmds.button(buffer, q=1, bgc=1) if bgc == [0.6, 0.6, 0.6]: cmds.button(buffer, e=1, bgc=[0.4, 0.4, 0.4]) else: cmds.button(buffer,e=1, bgc=[0.6, 0.6, 0.6]) Left_Arrow = cmds.button(label="<", h=25, w=25, bgc=Normal_color) # no change Initial_Page = cmds.button( label="1", h=25, w=25, bgc=[0.6, 0.6, 0.6], c=button_change ) Left_Excess = cmds.button(label="2", h=25, w=25, bgc=Normal_color, c=button_change) First_Middle_option = cmds.button( label="3", h=25, w=25, bgc=Normal_color, c=button_change ) Second_Middle_option = cmds.button( label="4", h=25, w=25, bgc=Normal_color, c=button_change ) Third_Middle_option = cmds.button( label="5", h=25, w=25, bgc=Normal_color, c=button_change ) Fourth_Middle_option = cmds.button( label="6", h=25, w=25, bgc=Normal_color, c=button_change ) Right_Excess = cmds.button(label="7", h=25, w=25, bgc=Normal_color, c=button_change) Last_Page = cmds.button(label="8", h=25, w=25, bgc=Normal_color, c=button_change) Right_Arrow = cmds.button(label=">", h=25, w=25, bgc=Normal_color) # no change all_button = [ Initial_Page, Left_Excess, First_Middle_option, Second_Middle_option, Third_Middle_option, Fourth_Middle_option, Right_Excess, Last_Page, ] button_ui([0.4, 0.4, 0.4]) cmds.formLayout( form, edit=True, attachForm=[(flow_layout, "top", 620), (flow_layout, "left", 300)] ) cmds.setParent("..") # flow cmds.setParent("..") # form # flip--- cmds.rowColumnLayout(numberOfColumns=5, h=690, bgc=[0.5, 0.5, 0.5]) # list---- cmds.setParent("..") # list---- cmds.setParent("..") # workspace---- cmds.setParent("..") # body---- cmds.window(windowName, e=1, w=1280, h=720, mxb=False, s=False) cmds.showWindow(windowName)
06-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值