【算法競賽】狀態壓縮型背包問題經典應用(藍橋杯2019A4分糖果)

在藍橋杯中遇到的這道題,看上去比較普通,但其實蘊含了很巧妙的“狀態壓縮 + 背包”的思想,本文將從零到一,詳細解析這個問題。

目錄

一、題目?

二、思路分析:狀態壓縮 + 最小覆蓋

1. 本質:最小集合覆蓋問題

2. 狀態設計:壓縮子集狀態

3. 狀態轉移邏輯

4. 為什么不用回溯解決?

三、狀壓DP通用壓縮和轉移技巧

1. 應用場景

2. 狀壓DP的核心構建思維

3. 狀態遍歷技巧

四、代碼

五、關鍵點總結


一、題目?

4.糖果 - 藍橋云課

?【翻譯過來就是】:

店里有 M 種不同口味的糖果。老板不單賣糖果,而是將 K 顆糖果打包成一包,并且每包糖果的口味可以預知。小明希望通過購買若干包糖果,品嘗到所有 M 種口味

我們的目標是:求出小明最少要買多少包糖果,才能嘗到所有口味?
若無論如何都無法湊齊所有口味,輸出 -1

二、思路分析:狀態壓縮 + 最小覆蓋

1. 本質:最小集合覆蓋問題

這道題本質上是一個最小集合覆蓋問題

給定 M 個元素的全集,每個集合(糖果包)覆蓋部分元素,求用最少的集合數量,使得它們的并集恰好覆蓋全集。

這類問題是組合優化中非常典型的 NP 完全問題,而這道題因為數據規模小(M ≤ 20),我們可以用狀態壓縮優化搜索空間

2. 狀態設計:壓縮子集狀態

考慮全集是口味 {1, 2, ..., M},那么每種“已經吃到的口味組合”可以表示為一個二進制串,長度為 M

  • i 位為 1 表示第 i 種口味已吃;

  • i 位為 0 表示第 i 種口味未吃;

  • 例如 01101 表示嘗到了第 0、2、3 種口味。

于是我們就可以設計出如下的狀態壓縮動態規劃:

  • 狀態定義dp[s] 表示要想吃到狀態 s 表示的口味集合,最少要買多少包糖果;

  • 狀態個數:全集口味數為 M,所以一共有 2^M 個狀態

  • 初始狀態dp[0] = 0 表示什么都沒吃;

  • 最終目標:我們要找出 dp[full],其中 full=(1<<M)-1,即全1,代表吃全所有口味

3. 狀態轉移邏輯

我們遍歷每一包糖果(相當于“可以選的物品”),如果當前狀態是 j,那么加入這一包糖果之后,會變成新狀態 j|a[i]

轉移公式如下:

dp[j|a[i]] = min(dp[j|a[i]], dp[j]+ 1)

意思是:“如果我當前吃到的是狀態j,現在加上這包糖果 a[i],我就能達到一個新的狀態j |a[i],那到達新狀態的最小糖果包數,可能是之前狀態的基礎上+1

為了避免狀態轉移時污染原始狀態(避免用新的 dp[j|a[i]]去影響其它轉移),我們從大到小遍歷狀態 j

4. 為什么不用回溯解決?

很多讀者第一時間可能想到回溯 + 剪枝,但那樣復雜度是指數級的

這題可以用動態規劃,是因為

  • 狀態可以表示為一個整數,并且狀態間的轉移有明顯結構

  • 每一步的選擇(糖果包)都可以使得狀態“合并”;

  • 我們可以用子問題的最優解構造出大問題的最優解,符合DP的子結構性質。

三、狀壓DP通用壓縮和轉移技巧

狀態壓縮 DP是競賽算法中經常考的一種技巧,尤其當狀態具有“組合”性質時非常常見

1. 應用場景

  • 集合覆蓋類問題(如本題)

  • 旅行商問題(TSP)

  • 開關燈問題 / 翻牌問題

  • 博弈論中已訪問節點記錄

  • 圖上路徑問題中“走過哪些點”狀態的保存

2. 狀壓DP的核心構建思維

  1. 狀態壓縮

    • 使用一個整數的二進制表示“某種集合”

    • 長度為 M 的集合有 2^M 個子集,對應 0~2^M - 12^M 個整數

  2. 狀態定義

    • 每一個狀態代表一種“中間形態”,可以是路徑、集合、排列的某種子結構

    • dp[mask] 表示從起點出發,達到狀態 mask 所需要的最小代價/方案數等

  3. 狀態轉移

    • 通常通過 dp[old_mask]->dp[new_mask] 來實現

    • new_mask=old_mask|(1<<x) 表示在old_mask 的基礎上加入一個新元素

    • 利用位運算快速合并和判斷狀態(如:是否包含、是否重疊等)

3. 狀態遍歷技巧

接下來給大家附上一下狀態壓縮常用的小技巧

遍歷目標寫法
遍歷所有狀態for (int mask = 0; mask < (1 << M); mask++)
遍歷 mask 的所有子集for (int sub = mask; sub; sub = (sub - 1) & mask)
檢查 mask 是否包含第 iif (mask & (1 << i))
加入元素 imask | (1 << i)
刪除元素 imask & ~(1 << i)

四、代碼

#include <iostream>
#include <cstring>
#include <algorithm> 
#include <climits>
using namespace std;
long long dp[(1<<20)+1];//左移20位
int n,m,k;
int a[110];//第i包所有的種類 
int main()
{fill(dp,dp+(1<<20)+1,INT_MAX);//初始化為最大值cin>>n>>m>>k;//n包糖果,m種口味,每包k種int c[25];//占位數組,是否能全部嘗到fill(c,c+m,0);bool no=false;//是否能全部嘗到//讀入n包糖果for(int i=1;i<=n;i++){int x=0,y;for(int j=1;j<=k;j++){cin>>y;//轉換為二進制//(1后面有y-1個0,相當于第y位是1) y--;x|=(1<<y); //先檢驗糖果是否全 //索引向后移一位 c[y]++;}a[i]=x;} //首先檢驗糖果種類是否齊全for(int i=0;i<m;i++){if(!c[i]) no=true;} if(no) cout<<"-1";else{//背包問題,裝i包糖果使每一種口味都能嘗到 dp[0]=0;//遍歷每一包 for(int i=1;i<=n;i++){//狀態從大到小 //背包問題,滾動數組背包容量從大到小 for(int j=(1<<m)-1;j>=0;j--){//是否選第i包 dp[j|a[i]]=min(dp[j|a[i]],dp[j]+1);}}//0-m-1位都為1 cout<<dp[(1<<m)-1];}return 0;
} 

五、關鍵點總結

技巧說明
狀態壓縮將口味組合轉換為一個整數表示
背包狀態轉移dp[new_state] = min(dp[new_state], dp[old_state] + 1)
滾動數組優化狀態轉移從大到小,避免重復選擇
初始化技巧INT_MAX 代表當前狀態不可達,一定要用 long long 存儲,否則可能溢出

這個問題很好的考察了對“狀態壓縮 + 背包模型”的掌握,也是一道藍橋杯常考的“最小覆蓋子集”變形題,可以作為一道練手題

如果你覺得本文對你有幫助,歡迎點贊收藏!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76897.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76897.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76897.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

STL 性能優化實戰:解決項目中標準模板庫的性能瓶頸

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家、全棧領域優質創作者、高級開發工程師、高級信息系統項目管理師、系統架構師&#xff0c;數學與應用數學專業&#xff0c;10年以上多種混合語言開發經驗&#xff0c;從事DICOM醫學影像開發領域多年&#xff0c;熟悉DICOM協議及…

大模型如何優化數字人的實時交互與情感表達

標題:大模型如何優化數字人的實時交互與情感表達 內容:1.摘要 隨著人工智能技術的飛速發展&#xff0c;數字人在多個領域的應用愈發廣泛&#xff0c;其實時交互與情感表達能力成為提升用戶體驗的關鍵因素。本文旨在探討大模型如何優化數字人的實時交互與情感表達。通過分析大模…

qt designer 軟件主題程序設計

對于使用Qt Designer設計的界面&#xff0c;主題切換的實現需要結合Qt的信號槽機制、樣式表動態加載以及資源管理。以下是針對Qt Designer UI的詳細解決方案&#xff1a; 一、UI文件與主題系統的整合架構 二、核心實現步驟 1. 動態樣式表加載系統 // ThemeManager.h class …

一、STM32簡介

一、實驗器材介紹 二、STM32簡介 1.STM32 名詞解釋 STM32是ST公司基于ARM Cortex-M內核開發的32位微控制器。 ST&#xff0c;指ST公司&#xff08;意法半導體&#xff09;;M&#xff0c;MicroController 微控制器&#xff08;MCU,MicroController Unit 微控制器單元/單片機&…

JVM虛擬機篇(一)深入理解JVM:組成部分、運行流程及程序計數器詳解

JVM虛擬機篇&#xff08;一&#xff09;深入理解JVM&#xff1a;組成部分、運行流程及程序計數器詳解 JVM虛擬機篇&#xff08;一&#xff09;深入理解JVM&#xff1a;組成部分、運行流程及程序計數器詳解一、引言二、JVM的組成部分2.1 類加載子系統2.2 運行時數據區2.3 執行引…

elementui的默認樣式修改

今天用element ui &#xff0c;做了個消息提示&#xff0c;發現提示的位置總是在上面&#xff0c;如圖&#xff1a; 可是我想讓提示的位置到下面來&#xff0c;該怎么辦&#xff1f; 最后還是看了官方的api 原來有個自定義樣式屬性 customClass 設置下就好了 js代碼 css代碼 效…

游戲引擎學習第204天

回顧并為今天的內容做鋪墊 好&#xff0c;現在開始這一集。今天我們將進行一些用戶界面編程&#xff0c;覺得這是一個展示如何編寫這類代碼的好時機。很多人對如何做用戶界面代碼都很好奇&#xff0c;所以展示一下如何編寫是非常有意義的。 我之所以在現在的這個地方做這些工…

我的世界1.20.1forge模組開發進階教程——TerraBlender

TerraBlender介紹 從模組開發者的視角來看,TerraBlender為Minecraft生物群系類模組的開發提供了全方位的技術支持,顯著降低了開發門檻并提升了模組的質量與擴展性: 跨平臺兼容性架構支持Forge/Fabric/Quilt/NeoForge四大主流加載器,開發者無需為不同平臺單獨適配代碼客戶端…

借助mcpo在open-webui中使用mcp

open-webui前幾天發布了0.6版本&#xff0c;我立即進行了升級。新版本中一個重要功能是通過mcpo方式支持了mcp server。本文將介紹mcpo是什么&#xff0c;以及如何在open-webui中使用它。同時&#xff0c;我也會分享幾個在接入過程中遇到的問題及解決方案。 首先來介紹mcpo&…

安裝gpu版本的dgl

1.先去網址&#xff0c;找到對應版本的dgl,然后下載到本地。 dgl-whl下載地址 我的是python 3.8 &#xff0c;cuda 11.6. windows 2.在虛擬環境里 輸入 pip install E:\dgl-1.0.2cu116-cp38-cp38-win_amd64.whl &#xff08;因為我下載到E盤里了&#xff09; 這樣GPU版本的d…

PyTorch使用(7)-張量常見運算函數

1. 基本數學運算 1.1 平方根和冪運算 import torchx torch.tensor([4.0, 9.0, 16.0])# 平方根 sqrt_x torch.sqrt(x) # tensor([2., 3., 4.])# 平方 square_x torch.square(x) # tensor([16., 81., 256.])# 任意冪次 pow_x torch.pow(x, 3) # tensor([64., 729., 4096…

Nginx功能及應用全解:從負載均衡到反向代理的全面剖析

Nginx作為一款開源的高性能HTTP服務器和反向代理服務器&#xff0c;憑借其高效的資源利用率和靈活的配置方式&#xff0c;已成為互聯網領域中最受歡迎的Web服務器之一。無論是作為HTTP服務器、負載均衡器&#xff0c;還是作為反向代理和緩存服務器&#xff0c;Nginx的多種功能廣…

安徽京準:NTP時間同步服務器操作使用說明

安徽京準&#xff1a;NTP時間同步服務器操作使用說明 3.1 連接天線 天線連接到“ANT”口。 3.2 連接電源 將220V電源線連到AC220V座上或將電源適配器&#xff08;7.5V~12V&#xff09;接到DC口上。也可以同時接上&#xff0c;提高供電可靠性。 3.3 LAN網口 網線連接到NTP…

Java項目之基于ssm的懷舊唱片售賣系統(源碼+文檔)

項目簡介 懷舊唱片售賣系統實現了以下功能&#xff1a; 用戶信息管理&#xff1a; 用戶信息新增&#xff1a;添加新用戶的信息。 用戶信息修改&#xff1a;對現有用戶信息進行修改。 商品信息管理&#xff1a; 商品信息添加&#xff1a;增加新的商品&#xff08;唱片&#x…

基于 Python 的自然語言處理系列(70):檢索增強生成(RAG)

1. 什么是 RAG&#xff1f; 在許多大模型&#xff08;LLM&#xff09;應用場景中&#xff0c;我們需要使用特定的用戶數據&#xff0c;而這些數據并未包含在模型的訓練集中。檢索增強生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09;是一種有效的解…

CAD插件實現:所有文字顯示到列表、縮放、編輯——CAD-c#二次開發

當圖中有大量文字&#xff0c;需要全部顯示到一個列表時并縮放到需要的文字時&#xff0c;可采用插件實現&#xff0c;效果如下&#xff1a; 附部分代碼如下&#xff1a; private void BtnSelectText_Click(object sender, EventArgs e){var doc Application.DocumentManager.…

Systemd構建自動化備份服務與外部存儲管理

實訓背景 你是一家數據公司的系統管理員&#xff0c;需設計一套自動化備份系統&#xff0c;滿足以下需求&#xff1a; 定期備份&#xff1a;每周日凌晨1點將 /data 目錄壓縮備份到 /backups。外部存儲掛載&#xff1a;插入USB設備時自動掛載到 /mnt/usb&#xff0c;并觸發增量…

PostgreSQL中根據另一表的值來更新一個字段

UPDATE table1 SET value t2.new_value FROM table2 t2 WHERE table1.id t2.reference_id; 解釋 UPDATE table1&#xff1a;指定要更新的表&#xff0c;不要用別名。 SET value t2.new_value&#xff1a;設置要更新的字段及其新值&#xff0c;這里新值來自 table2。也可更…

#SVA語法滴水穿石# (000)斷言基本概念和背景

一、前言 隨著數字電路規模越來越大、設計越來越復雜,使得對設計的功能驗證越來越重要。首先,我們要明白為什么要對設計進行驗證?驗證有什么作用?例如,在用FPGA進行設計時,我們并不能確保設計出來的東西沒有功能上的漏洞,因此在設計后我們都會對其進行驗證仿真。換句話說…

Git 從入門到精通(開源協作特別版)

&#x1f9e0; Git 從入門到精通&#xff08;開源協作特別版&#xff09; ? 基礎命令 &#x1f9f0; 高級用法 &#x1f6e0;? 開源實戰技巧 &#x1f30d; GitHub 社區協作 適合&#xff1a;從0開始 → 熟練開發者 → 參與/維護開源項目 &#x1f530; 第1章&#xff1a;…