2021年中國大學生程序設計競賽女生專場 C. 連鎖商店 (狀壓dp)


  • 題意:每個點都屬於某個公司,公司對應一個權值,對於一條路徑,如果一些點屬於同一家公司,那么貢獻只能算一次,給你一張圖,路徑只能從小的往大的走,現在問你從\(1\)到每個點的路徑上的最大權值是多少。

  • 題解\(n\)最大為\(36\),出現多個點的公司數最大為\(\frac{n}{2}\),不難發現,對於一條路徑,如果這條路徑上的一些點僅有一家公司屬於他們,這種情況是固定的,出現狀態分裂的情況為出現多個點的那些公司,而這題的\(n\)很小,我們可以用二進制來壓縮出現多個點的公司的狀態。

    \(dp[i][j]\)表示,當前在\(i\)點,出現多個點的公司的選擇情況為\(j\)\(to\)表示上一個點,那么有:

    1.\(i\)只有一家公司擁有他,那么我們直接從上一個點轉移即可,\(dp[i][j]=max(dp[to][j]+w[c[i]])\).

    2.\(i\)屬於出現多個點的公司,那么先找到他在出現多個點的公司的新編號,那么根據當前的\(j\)直接轉移就好

    具體看代碼吧,這里注意,有的人可能會說,假如我找到\(i\)的時候,\(j\)的狀態表示了后面一些還沒跑到的點會不會有問題,這里是沒問題的,因為只有跑過的點會對狀態有影響,沒有遍歷到的點選還是不選都一個樣,因為值都是一樣的

  • 代碼

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 2e6 + 10;
    const int mod = 998244353;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n,m;
    int c[N],w[N];
    int dp[40][N];
    vector<int> edge[N];
    unordered_map<int,int> mp;
    vector<int> v;
    int main() {
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;++i){
    		scanf("%d",&c[i]);
    		mp[c[i]]++;
    	}
    	for(int i=1;i<=n;++i) scanf("%d",&w[i]);
    	for(auto w:mp){
    		if(w.se>1) v.pb(w.fi);
    	}
    	for(int i=1;i<=m;++i){
    		int u,v;
    		scanf("%d %d",&u,&v);
    		edge[v].pb(u);
    	}
    	edge[1].pb(0);
    	for(int i=1;i<=n;++i){
    		int id=-1;
    		for(int j=0;j<(int)v.size();++j){
    			if(c[i]==v[j]){
    				id=j;
    				break;
    			}
    		}
    		if(id==-1){
    			for(int j=0;j<(1<<(int)v.size());++j){
    				for(auto to:edge[i]){
    					dp[i][j]=max(dp[i][j],dp[to][j]+w[c[i]]);
    				}
    			}
    		}
    		else{
    			for(int j=0;j<(1<<(int)v.size());++j){
    				if(j&(1<<id)){
    					for(auto to:edge[i]){
    						dp[i][j]=max(dp[i][j],dp[to][j^(1<<id)]+w[c[i]]);
    					}
    				}
    				else{
    					for(auto to:edge[i]){
    						dp[i][j]=max(dp[i][j],dp[to][j]);
    					}
    				}
    			}
    		}
    		int ans=0;
    		for(int j=0;j<(1<<(int)v.size());++j) ans=max(ans,dp[i][j]);
    
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM