刷题记录 --dp篇

刷题记录 --dp篇,第1张

最近在复习之前学过的算法,也有很多之前没接触过的。为了方便复习,把以后刷的题 不论难易都搬过来。
目前在洛谷上面做题,后面应该会做一做cf和leetcode上的比赛
这几天在做的是DP(动态规划),这个之前也没系统的刷过题,自己不看题解基本上是做不出来的,所以多刷一些,提升一下思维能力。
现在刷的这个题单,不从头记录了,就从今天开始把。
第一题:P1176 路径计数2
题目描述

一个N×N 的网格,你一开始在(1,1),即左上角。每次只能移动到下方相邻的格子或者右方相邻的格子,问到达(N,N),即右下角有多少种方法。
但是这个问题太简单了,所以现在有M个格子上有障碍,即不能走到这M个格子上。
从左上角走到右下角,每次只能向右或者向下,某些格子无法行走,求方案数。
本题是橙题,思路比较直接
对于正常格子 状态方程为 dp[i][j]=dp[i-1][j]+dp[i][j-1]
障碍格子 dp[i][j]=0;

#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=100010;
const ll mod=100003;
int read(){//读入优化
	int x=0;bool f=0;char c=getchar();
	while (c<'0'||c>'9'){if (c=='-')f=1;c=getchar();}
	while (c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
int n,m;
bool a[1010][1010];
int dp[1010][1010]; 
int main(){
	cin>>n>>m;
	int x,y;
	memset(a,false,sizeof(a));
	for(int i=1;i<=m;i++) cin>>x>>y,a[x][y]=true;
    dp[0][1]=1;//可以这样初始化  也可以初始化dp[1][1],但对于出发点需要特判
    for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(!a[i][j]) dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
		}
	}
	cout<

第二题 P1420 最长连号
题目描述

输入长度为 n 的一个正整数序列,要求输出序列中最长连号的长度。

连号指在序列中,从小到大的连续自然数。

输出给定序列的最长上升子序列,相邻数必须相差1
我的思路是遍历一遍,用一个变量记录当前状态 每次不符合条件时修改为1,
还有一种方法在求最长上升子序列O(n^2)基础上 进行特判,

#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=100010;
const ll mod=100003;
int read(){//读入优化
	int x=0;bool f=0;char c=getchar();
	while (c<'0'||c>'9'){if (c=='-')f=1;c=getchar();}
	while (c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
int n;
int a[maxn];
int main(){
	int ans=-1,cnt=1;
	cin>>n;
	a[0]=-2;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		if(a[i]==a[i-1]+1)
		{
			cnt++;
		}
		else
		{
			if(cnt>ans) ans=cnt;
			cnt=1;
		}
	}
	cout<

第三题P2602 [ZJOI2010] 数字计数
题目描述

给定两个正整数 a 和 b,求在 [a,b]中的所有整数中,每个数码(digit)各出现了多少次。

a,b的范围是 12次方 O(n) 肯定行不通 , 自己想不出来正解,数位DP之前也没有做过,盯着看了一天才看题解敲了出来
其他题解中的状态转移方程还是看不懂

#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=100010;
const ll mod=998244353;
int read(){//读入优化
	int x=0;bool f=0;char c=getchar();
	while (c<'0'||c>'9'){if (c=='-')f=1;c=getchar();}
	while (c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
ll a1,b;
ll a2[1000];
ll getC(ll x,int t)//0-x中,  t出现的次数
{
	memset(a2,0,sizeof(a2));
	 ll  m=1;
	  while(mt) a2[t]+=(a+1)*m;
	  		else if(b==t) a2[t]+=a*m+c+1;
	  		else a2[t]+=a*m;
	  	}
	  	else
	  	{
	  		if(b) a2[t]+=a*m;
	  		else a2[t]+=(a-1)*m+c+1;
	  	}
	  	m*=10;
	  }
	  return a2[t];
}
int main(){
	cin>>a1>>b;
	for(int i=0;i<=9;i++)  //f(a-b) = f(0-b)-f(0-a-1)
	if(i!=0) cout<<" "<

第四题 P2782 友好城市
题目描述

有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航道不相交的情况下,被批准的申请尽量多
南北两岸有n条线,在不相交的情况下最多可以保留多少。
首先按北岸城市递增排序,则结果为南岸对应序号的最长上升子序列

#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=1000010;
const ll mod=998244353;
int read(){//读入优化
	int x=0;bool f=0;char c=getchar();
	while (c<'0'||c>'9'){if (c=='-')f=1;c=getchar();}
	while (c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
struct node{ //按结构体保存  
	int a,b;
}a[maxn]; 
bool cmp(node a,node b)  //按a排序  b其实也可以
{
	return a.a>n;
	for(int i=1;i<=n;i++) cin>>a[i].a>>a[i].b;
	sort(a+1,a+1+n,cmp);
	for(int i=1;i<=n;i++) b[i]=a[i].b;
	for(int i=1;i<=n;i++)//求解最长上升子序列的过程    n^2  会超时,所以这里建一个额外数组二分进行查找
	{
       if(b[i]>c[cnt]) c[++cnt]=b[i];
       else
       {
       	  int site=upper_bound(c+1,c+1+cnt,b[i])-c;
		   c[site]=b[i];	       	
       }
	}
	cout<

第五题P1137 旅行计划
不加题面了
是一个有向无环图,以某个点为终点的最长路径
反转一下 ,求某点出发的最长路径 记忆化搜索 dp[i]=max(dp[i],dp[i.to]+1)
如果正着做进行dp 应该是需要进行拓扑排序 以确保在计算x之后 后面的节点不会影响之前的结果。拓扑排序还没有写过代码 ,,,

#include
#include
using namespace std;
typedef long long ll;
const int maxn=100010;
const ll mod=998244353;
int n,m;
ll book[1000010];
vectora[maxn]; //vector存图 挺方便的
ll dfs(int x)
{
	if(book[x]!=-1) return book[x];
	book[x]=1;
	for(int i=0;i>n>>m;
	int x,y;
	memset(book,-1,sizeof(book));
	for(int i=1;i<=m;i++) cin>>x>>y,a[y].push_back(x); //这里是反向存图
	for(int i=1;i<=n;i++)
	cout<

第六题 P3183 [HAOI2016]食物链
同样是有向无环图 找入度为0的点到出度为0的点的路径数
最直接的做法还是搜索 ,第一次没有加记忆化T了 70分
加上就A了
题里已经给出 孤立节点不需要计算 所以搜索前判断一下该节点是否出度为0

#include
#include
using namespace std;
typedef long long ll;
const int maxn=100010;
const ll mod=998244353;
int n,m,ans=0;
vectorv[maxn];
bool book[maxn];
int dp[maxn];
int dfs(int x) //
{
	if(dp[x]!=-1) return dp[x];
	if(v[x].empty()) return 1;
	int ans=0;
	for(int i=0;i>n>>m;
	for(int i=1;i<=m;i++) cin>>a>>b,v[a].push_back(b);
	memset(dp,-1,sizeof(dp));
	for(int i=1;i<=n;i++)
	{
		if(!book[i]&&v[i].size()>0) ans+=dfs(i);
	}
	cout<

这道题还有拓扑排序的做法
自己码了一遍 ,尝试着用链式前向星存图
这里不需要保存拓扑排序的结果,直接把当前节点进行转移

#include
#include
using namespace std;
typedef long long ll;
const int maxn=200010;
const ll mod=998244353;
int n,m;
int topo[maxn];
int cnt=0,ans[maxn],head[maxn],in[maxn],out[maxn];
struct Edge{
	int u,v,next;
}e[maxn];
int total=0;
void add(int x,int y)
{
	e[++cnt].u=x;
	e[cnt].v=y;
	e[cnt].next=head[x];
	head[x]=cnt;
}
void topsort(int k)
{
	for(int i=head[k];i;i=e[i].next) 
	{
		ans[e[i].v]+=ans[k];
		if(!--in[e[i].v]) topsort(e[i].v);
	}
}
int main(){
	int a,b;
	cin>>n>>m;
	for(int i=1;i<=m;i++)  cin>>a>>b,add(a,b),++in[b],++out[a];
	
	for(int i=1;i<=n;i++) 
	{
		if(!in[i]&&out[i]&&!ans[i]) 
		{
			ans[i]=1;
			topsort(i);
		}
	}
	for(int i=1;i<=n;i++) if(!out[i])  total+=ans[i];
	cout<

第七题 P2017 [USACO09DEC]Dizzy Cows G
给定一个图,有无向边和有向边,把无向边转化为有向边,使得整个图是无环的。
在有向无环图上进行拓扑排序,得到每个顶点的拓扑顺序,对于无向边的两个顶点的指向,由这两个点的拓扑顺序所确定。
这里拓扑排序保存的不是顶点,而是每个顶点在拓扑序中的相对位置。

#include
#include
using namespace std;
typedef long long ll;
const int maxn=200010;
const ll mod=998244353;
int n,p1,p2;
int cnt=0,ans[maxn],head[maxn],in[maxn],pre[maxn];
struct Edge{
	int u,v,next;
}e[maxn];
int total=0;
void add(int x,int y)
{
	e[++cnt].u=x;
	e[cnt].v=y;
	e[cnt].next=head[x];
	head[x]=cnt;
}
int main(){
	int a,b,t=0;
	cin>>n>>p1>>p2;
	for(int i=1;i<=p1;i++)  cin>>a>>b,add(a,b),++in[b];
    queueq;
    for(int i=1;i<=n;i++) if(in[i]==0) q.push(i);
    while(!q.empty())
    {
    	int x=q.front();
    	q.pop();
    	ans[x]=t++;
    	for(int i=head[x];i;i=e[i].next)
    	{
    		 --in[e[i].v];
    		 if(in[e[i].v]==0) q.push(e[i].v);
    	}
    }
	for(int i=1;i<=p2;i++)
	{
		cin>>a>>b;
		if(ans[a]

话说怎么dp专题 做出一堆拓扑排序的题

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/799014.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-06
下一篇2022-05-06

发表评论

登录后才能评论

评论列表(0条)