基本思想:是用f[i][j][k]表示当前i行的状态,j表示当前这一行的棋子状态,k表现棋盘目前全部的棋子。因此我们可以建立状态关系f[i][j][k]=f[i-1][x][k-num[j]]。并且我们要注意,一开始对一列状态的初始化也十分重要,dfs过去,对能成立的状态进行存储。并且这题要注意用long long存储(在这个地方死了好几次了)。代码如下
#include <iostream>
using namespace std;
const int N = 9;
const int M = 512;
long long state[M], number[M];
long long f[15][M][90];
int n, m;
int cnt;
void dfs(int x, int num, int
cur) { //x是当前状态,num是当前状态的棋子个数,cur是指针,但是是从0-n-1
if (cur >= n) {
state[cnt] = x;
number[cnt++] = num;
return;
}
dfs(x, num, cur + 1);//当前位置不放国王,所以状态和数字都不会改变
dfs(x + (1 << cur), num + 1, cur + 2);//当前位置放国王
}
bool compatible(int x, int y) {
if (state[x] & state[y])
return false;
if ((state[x] >> 1) & state[y])
return false;
if (state[x] & (state[y] >> 1))
return false;
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
dfs(0, 0, 0);
for (int i = 0; i < cnt; i++)
f[1][state[i]][number[i]] = 1;
for (int i = 2; i <= n; i++)
for (int j = 0; j < cnt; j++)
for (int t = 0; t < cnt; t++) {
if (compatible(j, t)) {
for (int k = number[j]; k <= m; k++)
f[i][state[j]][k] += f[i - 1][state[t]][k - number[j]];
}
}
long long ans = 0;
for (int i = 0; i < cnt; i++)
ans += f[n][state[i]][m];
cout << ans << endl;
return 0;
}
基本思想:因为是不同时间过桥的,所以其实这里的dp数组只需要一维就可以了。然后学习到了一些技巧和问题。首先是这里的const int N=1<<n,必须要在输入n之后,再初始化,不然会出现问题。其次这题也需要对一开始的状态初始化,对每个状态进行求解时间和重量。在进入最后的判断阶段的时候。先初始化一个dp数组。这里的dp数组包括ts,ws数组都是vector动态创建的,然后遍历每一个状态,如果当前状态重量小于桥重量,就可以初始化dp为此时的时间。接着这里用了一个很方便的循环,令j=i;j;j=i&(j-1).这样就能遍历i的所有子集,牛逼。i^j其实就相当于集合i-集合j。
if (ws[i ^ j] <= W)dp[i] = min(dp[i], dp[j] + ts[i ^ j]);//i^j is i-j
#include<iostream>
#include<vector>
using namespace std;
int t,w;
int W, n;
//const int N = 1 << n;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> W >> n;
const int N = 1 << n;
vector<int> ts(N + 1), ws(N + 1);
for (int i = 0; i < n; i++)
{
cin >> t >> w;
for (int j = 0; j < N; j++)
{
if (j & (1 << i))
{
ts[j] = max(ts[j], t);
ws[j] += w;
}
}
}
vector<int> dp(N + 1, 0x3f3f3f3f);
for (int i = 0; i < N; i++)
{
if (ws[i] <= W)dp[i] = ts[i];
for (int j = i; j; j = i & (j - 1))//list all condition
if (ws[i ^ j] <= W)dp[i] = min(dp[i], dp[j] + ts[i ^ j]);//i^j is i-j
}
cout << dp[N - 1] << endl;
return 0;
}
复习的时候,也可以参考acwing基础课中 蒙德里安的梦想和最短hamilton路径
玉米田 也是自己做出来了。但卡了半小时bug,原因是dp[i][j],这里的j表示状态,但我直接用索引加上去了。代码如下
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int M = 13;
const int mod = 1e8;
int soil[M][M];
int n, m;
int dp[M][1 << M];
vector<int>state[M];
bool compatible(int x, int y)
{
if (x & y)return false;
return true;
}
void dfs(int hang,int x,int point)
{
if (point >= m)
{
state[hang].emplace_back(x);
return;
}
if(soil[hang][point]==1) dfs(hang, x + (1 << point), point + 2);
dfs(hang, x, point + 1);
}
void work()
{
memset(soil, 0, sizeof soil);
cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)cin >> soil[i][j];
for (int i = 0; i < n; i++)dfs(i, 0, 0);
for (int i = 0; i < state[0].size(); i++)dp[0][state[0][i]] = 1;
for (int i = 1; i < n; i++)
for (int j = 0; j < state[i].size(); j++)
{
int cnt = 0;
for (int k = 0; k < state[i - 1].size(); k++)
if (compatible(state[i][j], state[i - 1][k]))
{
dp[i][state[i][j]] += dp[i - 1][state[i-1][k]];
//cout << cnt++ << " ";
}
}
//cout << dp[1][0] <<" "<<dp[1][1]<< endl;
// for (int i = 0; i < state[0].size(); i++)cout << dp[0][state[0][i]] << " ";
int ans = 0;
for (int i = 0; i < state[n - 1].size(); i++)ans =(ans+ dp[n - 1][state[n - 1][i]])%mod;
cout << ans<<endl;
//cout << state[0].size() << " ";
//cout << state[1].size() << " " << state[1][1];
return;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
work();
return 0;
}
炮兵阵地 这道题不能用之前那种想法做了。之前的互不侵犯和玉米田,都可以基于上一层的状态来转移。但这个必须基于两层状态来转移。同时因为三层数组空间开太大,所以要使用转移数组。
代码如下
#include<iostream>
#include<vector>
using namespace std;
const int N = 101;
const int M = 10;
int n, m;
int f[2][1 << M][1 << M];
int g[N], cnt[1<<M];
vector<int> state;
vector<int> head[1<<M];
bool check(int x)
{
return !(x & (x >> 1) || x & (x >> 2));
}
int count(int x)
{
int ans = 0;
while (x)
{
ans += x & 1;
x >>= 1;
}
return ans;
}
void work()
{
cin >> n >> m;
vector<vector<char>> place(n, vector<char>(m));
for (int i = 1; i <= n; i++)
for (int j = 0; j < m; j++)
{
char c;
cin >> c;
if (c == 'H')g[i] += 1 << j;
}
for (int i = 0; i < (1 << m); i++)
{
if (check(i))state.emplace_back(i);
cnt[i] = count(i);
}
for (int cur_st : state)
for (int pre_st : state)
if (!(cur_st & pre_st))head[cur_st].emplace_back(pre_st);
for (int i = 1; i <= n; i++)
for (int st : state)
if (!(g[i] & st))
for (int p1 : head[st])
for (int p2 : head[p1])
if (!(st & p2))
f[i & 1][st][p1] = max(f[i & 1][st][p1], f[(i - 1) & 1][p1][p2]+cnt[st]);
int res = 0;
for (int st : state)
for (int pre : head[st])
res = max(res, f[n & 1][st][pre]);
cout << res;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
work();
return 0;
}
愤怒的小鸟 看题解说可以用dancinglink来做,上一次接触这个还是大一哎。但是因为小鸟的个数比较少<=18,所以可以用状压dp来做。用一维dp来存储。但是要先预处理抛物线,并且记录当前抛物线的状态。随后一层层遍历即可。对于每一个当前状态,更新的办法就是,找到当前状态中不存在的点,然后遍历这个点所有的抛物线来加到当前状态之上,然后判断。代码如下
小知识:浮点数无法准确表示,所以判断相等要用1e-8.绝对值要用fabs而不是abs
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef pair<double, double> PDD;
const int N = 18, M = 1 << 18;
const double eps = 1e-8;
int n, m;
PDD q[N];
int path[N][N];
int f[M];
int cmp(double x, double y)
{
if (fabs(x - y) < eps)return 0;
if (x < y)return -1;
return 1;
}
void work()
{
int T;
cin >> T;
while (T--)
{
cin >> n >> m;
for (int i = 0; i < n; i++)cin >> q[i].first >> q[i].second;
memset(path, 0, sizeof path);
for (int i = 0; i < n; i++)
{
path[i][i] = 1 << i;
for (int j = 0; j < n; j++)
{
double x1 = q[i].first, y1 = q[i].second;
double x2 = q[j].first, y2 = q[j].second;
if (!cmp(x1, x2))continue;
double a = (y1 / x1 - y2 / x2) / (x1 - x2);
double b = y1 / x1 - a * x1;
if (cmp(a, 0) >= 0)continue;
int state = 0;
for (int k = 0; k < n; k++)
{
double x = q[k].first, y = q[k].second;
if (!cmp(a * x * x + b * x, y))state += 1 << k;
}
path[i][j] = state;
}
}
memset(f, 0x3f, sizeof f);
f[0] = 0;
for (int i = 0; i < (1 << n) - 1; i++)
{
int x = 0;
for(int j=0;j<n;j++)
if (!(i >> j & 1))
{
x = j;
break;
}
for (int j = 0; j < n; j++)
f[i | path[x][j]] = min(f[i | path[x][j]], f[i] + 1);
}
cout << f[(1 << n) - 1] << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
work();
return 0;
}