当前位置 : 主页 > 网络编程 > PHP >

状压dp常用位运算

来源:互联网 收集:自由互联 发布时间:2023-09-07
​​ACM-ICPC 2018 南京赛区网络预赛 E. AC Challenge(状压dp)​​ 位运算 1.判断一个数字x二进制下第i位是不是等于1。 方法:if ( ( ( 1 ( i - 1 ) ) x ) 0) 将1左移i-1位,相当于制造了一个只有第


​​ACM-ICPC 2018 南京赛区网络预赛 E. AC Challenge(状压dp)​​


位运算

1.判断一个数字x二进制下第i位是不是等于1。

方法:if ( ( ( 1 << ( i - 1 ) ) & x ) > 0)

将1左移i-1位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。

2.将一个数字x二进制下第i位更改成1。

方法:x = x | ( 1<<(i-1) )

证明方法与1类似,此处不再重复证明。

3.把一个数字二进制下最靠右的第一个1去掉。

方法:x=x&(x-1)


4.把一个数字二进制下第i位置0

方法: x=x&~(1<<(i-1))

题目

有 n 个问题需要解决,但是每个一问题都有

状压dp常用位运算_数位

个前置问题需要先解决才行。每解决一个问题

状压dp常用位运算_位运算_02

花费一个时间,得到的分数位

状压dp常用位运算_位运算_03

。问最后能得到最大分数。

分析

用一个数字 i 表示一个状态,一个状态就是已经解决了哪些问题,

i 的第 j 位为 1 代表 i 状态解决了问题 j

转移方程很好写:

状压dp常用位运算_i++_04

,其中 s 代表 i 的第 j 个问题还没解决时。 即

状压dp常用位运算_位运算_05


枚举所有状态,在状态转移之前要分析当前状态 i 是否合理。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 20 + 5;

int n, a[N], b[N], si, tmp, tt;
vector<int> e[N]; // 存前置题目
int dp[1 << 20], ans = 0;

int main(){
memset(dp, -INF, sizeof(dp));
dp[0] = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d %d %d", &a[i], &b[i], &si);
for(int j = 1, x; j <= si; j++)
{
scanf("%d", &x);
e[i].push_back(x);
}
}
int s = (1 << n) - 1;
for(int i = 1; i <= s; i++){ // 对于所有状态
for(int j = 1; j <= n; j++){ // 判断每一个问题的情况
if(((1 << (j-1)) & i) == 0) // 若 i 第 j 位等于 0
continue;
for(int k = 0; k < e[j].size(); k++){ // 当前状态没有符合前置问题
if(((1 << (e[j][k] - 1)) & i) == 0) // 直接进行下一个状态 i
goto end;
}
}
tmp = i, tt = 0;
while(tmp){ // 当前状态解决了几个问题,
if(tmp & 1) // 相当于算下过了几分钟
tt++;
tmp >>= 1;
}
for(int j = 1; j <= n; j++){ // 对于 n 个问题
if(((1 << (j - 1)) & i) == 0) // 若第 j 位是 0
continue;
if(dp[i - (1 << (j-1))] != -INF)
dp[i] = max(dp[i], dp[i - (1 << (j - 1))] + tt * a[j] + b[j]);
}
ans = max(ans, dp[i]);
end: ;
}
printf("%d\n", ans);
return 0;
}

【本文由:香港云服务器 http://www.558idc.com/ne.html网络转载请说明出处】
上一篇:在公网任意地点,远程访问家中的群晖NAS
下一篇:没有了
网友评论