今日继续学习状态压缩DP和复习floyd以及树状数组,当数据过大时记得开longlong!!!!!!!!
棋盘型状态压缩DP一般需要判断行与行之间的关系,同时预处理出合法方案和合法的转移方案
然后再进行状态转移的计算
Acwing 327.玉米田
农夫约翰的土地由 M×N 个小方格组成,现在他要在土地里种植玉米。
非常遗憾,部分土地是不育的,无法种植。
而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。
现在给定土地的大小,请你求出共有多少种种植方法。
土地上什么都不种也算一种方法。
输入格式
第 1 行包含两个整数 M 和 N。
第 2..M+1 行:每行包含 N 个整数 0 或 1,用来描述整个土地的状况,1 表示该块土地肥沃,0 表示该块土地不育。
输出格式
输出总种植方法对 1e8 取模后的值。
数据范围
1≤M,N≤12
样例
2 3
1 1 1
0 1 0
输出
9
判断时间复杂度大概是n*2^n*2^n=2e8但是有一些情况是不合法的,所以一般能过
#include <bits/stdc++.h>
using namespace std;
const int N=13,M=1<<12,mod=1e8;
int f[N][M],s[N];//已经放了i行,第i行状态为s
vector<int> state;//合法状态
vector<int> h[M];//合法的转移状态
int n,m;
bool check(int state){
for(int i=0;i<m;i++)
if((state>>i&1)&&(state>>i+1&1)) return false;
return true;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=0;j<m;j++){
int t;cin>>t;
s[i]+=!t<<j;//保存每一行没有营养的土地
}
for(int i=0;i<1<<m;i++)
if(check(i))
state.push_back(i);
for(int i=0;i<state.size();i++)
for(int j=0;j<state.size();j++){
int a=state[i],b=state[j];
if((a&b)==0)
h[i].push_back(j);//直接用坐标映射
}
f[0][0]=1;//第0行不放一种
for(int i=1;i<=n+1;i++)
for(int j=0;j<state.size();j++){
int a=state[j];//第i行
if(a&s[i]) continue;
for(auto b:h[j]){//第i-1行
f[i][a]=(f[i][a]+f[i-1][state[b]])%mod;
}
}
cout<<f[n+1][0];
return 0;
}
P2704 [NOI2001] 炮兵阵地
题目描述
司令部的将军们打算在 N×M 的网格地图上部署他们的炮兵部队。
一个 N×M 的地图由 𝑁N 行 𝑀M 列组成,地图的每一格可能是山地(用 H 表示),也可能是平原(用 PP 表示),如下图。
在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
输入格式
第一行包含两个由空格分割开的正整数,分别表示 N 和 M。
接下来的 N 行,每一行含有连续的 M 个字符,按顺序表示地图中每一行的数据。
输出格式
一行一个整数,表示最多能摆放的炮兵部队的数量。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N=110,M=1<<10;
int f[2][M][M],cnt[M],s[N],n,m;//已经摆完第i行,第i-1行状态为j,第i行状态为k
vector<int> state;//一排的状态,中间的1代表放炮兵
vector<int> h[M];
bool check(int u){
for(int i=0;i<m;i++)
if((u>>i&1)&&((u>>i+1&1)|(u>>i+2&1))) return false;
return true;
}
int count(int a){
int res=0;
for(int i=0;i<m;i++) res+=a>>i&1;
return res;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=0;j<m;j++){
char c;
cin>>c;
if(c=='H')
s[i]+=1<<j;
}
for(int i=0;i<1<<m;i++)
if(check(i)){
state.push_back(i);
cnt[i] = count(i);//初始化出个数
}
for(int i=0;i<state.size();i++)
for(int j=0;j<state.size();j++){
int a=state[i],b=state[j];
if(!(a&b))
h[a].push_back(b);//设置状态转移
}
for(int i=1;i<=n+2;i++)
for(auto a:state)//第i行
for(auto b:h[a])//第i-1行
for(auto c:h[b]){//第i-2行
if(a&b|a&c|b&c) continue;//三行之间不能出现
if(s[i-1]&b|s[i]&a) continue;//当前枚举出被转移的状态是合法的也就是a,b都要保证不会被炸到
f[i&1][a][b]=max(f[i&1][a][b],f[i-1&1][b][c]+count(a));
}
cout<<f[n+2&1][0][0];
return 0;
}
树状数组模板
单点修改区间查询
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int a[N],n,m;
int lowbit(int x){
return x&-x;
}
void add(int p,int u){
for(int i=p;i<=n;i+=lowbit(i))
a[i]+=u;
}
int query(int p){
int ans=0;
for(int i=p;i>0;i-=lowbit(i))//一定是到i等于0就停止
ans+=a[i];
return ans;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){//从1开始读入
int x;cin>>x;
add(i,x);//读入
}
for(int i=0;i<m;i++){//操作
int op,a,b;
cin>>op>>a>>b;
if(op==1){
add(a,b);
}else if(op==2){
cout<<query(b)-query(a-1)<<'\n';
}
}
return 0;
}
区间修改单点查询
#include <iostream>
using namespace std;
const int N=5e5+10;
int t[N],a[N],n,m;
int lowbit(int x){
return x&-x;
}
void add(int p,int u){
for(int i=p;i<=n;i+=lowbit(i))
t[i]+=u;
}
int query(int p){
int ans=0;
for(int i=p;i;i-=lowbit(i))
ans+=t[i];
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
add(i,a[i]-a[i-1]);//差分化
}
for(int i=0;i<m;i++){//m次操作
int op;cin>>op;
if(op==1){
int x,y,k;cin>>x>>y>>k;
add(x,k);
add(y+1,-k);
}else if(op==2){
int x;cin>>x;
cout<<query(x)<<'\n';
}
}
return 0;
}
[ABC286E] Souvenir
Floyd存路径以及更新最大价值
#include <bits/stdc++.h>
using namespace std;
const int N=310;
long long g[N][N],n,a[N],w[N][N];
void floyd(){
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(g[i][j]>g[i][k]+g[k][j]){
g[i][j]=g[i][k]+g[k][j];
w[i][j]=w[i][k]+w[k][j]-a[k];//a[k]加了两次
}else if(g[i][j]==g[i][k]+g[k][j]){//距离相等时可能获得的价值会更大
w[i][j]=max(w[i][j],w[i][k]+w[k][j]-a[k]);
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
char ch;cin>>ch;
if(i==j)
{
w[i][j]=a[i];
g[i][j]=0;
}
else
{
if(ch=='Y')
{
g[i][j]=1;
w[i][j]=a[i]+a[j];
}
else
{
g[i][j]=0x3f3f3f3f;
w[i][j]=0;
}
}
}
}
floyd();
int q;cin>>q;
while(q--){
int u,v;cin>>u>>v;
if(g[u][v]<0x3f3f3f3f/2)//可能会有负权边
cout<<g[u][v]<<' '<<w[u][v]<<'\n';
else
cout<<"Impossible"<<'\n';
}
return 0;
}