codeforces1316E(状压dp,状态转移只需对所有状态来源取最大值)
解题思路:
这题需要用状压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
反思总结:
-
以后做动态规划的题目时要优先考虑有哪些状态,然后思考某一状态的所有来源取最值,最终状态就是答案了
-
若是状态太多则可以考虑用某一个数的比特位来表示