codeforces1316E(状压dp,状态转移只需对所有状态来源取最大值)

Problem - E - Codeforces

解题思路:

这题需要用状压dp,因为一个数可以表示成二进制的形式,二进制的每一位可以表示存在和不存在两种状态,通过用一个数表示就可以用位运算进行操作,这样可以省下大量的代码量,不然需要初始化\(dp[N][2][2][2][2][2][2][2]\)这样的状态变量。代码会变得非常冗杂。若是用状压dp的话只需初始化\(dp[N][1<<p]\),所以就考虑状态转移有

\[dp[i][000100]=max(dp[i-1][000100],dp[i-1][000100]+a[i],dp[i-1][000000]+s[i]) \]

\(dp[i-1][000100]+a[i]\)的前提是\(i<=k+bitcount(000100)\)

\(dp[i][000100]\)表示前\(i\)个人只有第四位有人的情况

最终答案就是\(dp[n][111111]\)

代码:


#include<bits/stdc++.h>
#define all(x) x.begin(),x.end()
#define int long long 
using namespace std;
const int N=1e5+5;
struct pe{
	int a;
	int s[7];
};
struct pe per[N];
bool cmp(pe a,pe b){
	return a.a>b.a;
}
int dp[N][130];
int countbit(int x){
	int ans=0;
	while(x){
		ans+=x&1;
		x>>=1;
	}
	return ans;
}
void solve(){
	int n,p,k;
	cin>>n>>p>>k;
	memset(dp,-0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=n;i++){
		cin>>per[i].a;
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<p;j++){
			cin>>per[i].s[j];
		}
	}
	sort(per+1,per+n+1,cmp);
	int full=1<<p;
	for(int i=1;i<=n;i++){
		for(int j=0;j<full;j++){
			int cnt=countbit(j);//已经有多少人被选为球员 
			dp[i][j]=dp[i-1][j];//什么也不做 
			if(i<=k+cnt)dp[i][j]=max(dp[i][j],dp[i-1][j]+per[i].a);//如果还可以选观众就放观众 
			for(int t=0;t<p;t++){
				int s=1;
				if((j>>t)&1){
					dp[i][j] = max(dp[i][j], dp[i - 1][j ^ (1 << t)] + per[i].s[t]);
					//倒数第t位一定都是1,^后就是0 
				}
			}
		}
	}
	cout<<dp[n][full-1]<<endl;
}
//dp[0][0000]
//dp[1][0000] dp[1][0001] dp[1][0010] dp[1][0100] dp[1][1000] 
//dp[2][0000] dp[2][0001] dp[2][0010] dp[2][0100] dp[2][1000] 
//dp[2][0000]来自dp[1][0000]
//dp[2][0001]来自dp[1][0000]或者dp[1][0001]; 
//一个状态只可能来自前面的状态 
//考虑一个状态的所有的可能来源,然后取他们来源中的最大值 
signed main() {
    ios::sync_with_stdio(0);
    cout.tie(0),cin.tie(0);
    int test=1;
    //cin>>test;
    while(test--)
    solve();
    return 0;
}

int

反思总结:

  • 以后做动态规划的题目时要优先考虑有哪些状态,然后思考某一状态的所有来源取最值,最终状态就是答案了

  • 若是状态太多则可以考虑用某一个数的比特位来表示

posted @ 2025-04-01 09:40  C微  阅读(36)  评论(0)    收藏  举报