今日总结2024/5/28

今日继续学习状态压缩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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值