1,走方格(dfs转dp);2,网络分析(对邻接表和bfs的使用)
1,走方格
题意:
找路径个数,dfs肯定可以,并且写着顺手,直接dfs,但是在(30,29)这个点tle了;
(正好复习下dfs板子,好久不写了)
#include<bits/stdc++.h>
#define rep1(i,a,n) for(register ll i=a;i<n;i++)
#define rep2(i,a,n) for(register ll i=a;i<=n;i++)
#define per1(i,n,a) for(register ll i=n;i>a;i--)
#define per2(i,n,a) for(register ll i=n;i>=a;i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define INF 0x3f3f3f3f
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
int n,m;
int f[110][110];
int ans;
int dx[]={0,1},dy[]={1,0};
bool canmove(int x,int y)
{
if(x>n||y>m||(x%2==0&&y%2==0))return 0;
return 1;
}
void dfs(int x,int y)
{
if(x==n&&y==m)
{
ans++;
return;
}
rep1(i,0,2)
{
int x1=x+dx[i],y1=y+dy[i];
if(canmove(x1,y1))dfs(x1,y1);
}
}
signed main()
{
quick_cin();
cin>>n>>m;
if(n%2==0&&m%2==0)cout<<0;
else
{
dfs(1,1);
cout<<ans;
}
return 0;
}
dfs不行,考虑dp
按照闫式dp分析法:
第一步:状态表示:
f[i , j ]表示从起点到1,1的到i ,j的方案数
属性:数量
第二步:状态计算:
先划分集合:根据最后一步的走法:有两种
要么从(i-1,j)向下走走到(i,j)要么从(i,j-1)向右走,走到(i,j)
这样的划分是不重不漏地;
然后计算
求子集,
左子集刚好为f[ i-1, j ] 右子集也是f[ i , j-1 ]
所以状态转移方程:f[ i , j ]=f[ i-1, j ] +f[ i ,j-1 ];
#include<bits/stdc++.h>
#define rep1(i,a,n) for(register ll i=a;i<n;i++)
#define rep2(i,a,n) for(register ll i=a;i<=n;i++)
#define per1(i,n,a) for(register ll i=n;i>a;i--)
#define per2(i,n,a) for(register ll i=n;i>=a;i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define INF 0x3f3f3f3f
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
int n,m;
int f[110][110];
signed main()
{
quick_cin();
cin>>n>>m;
f[1][1]=1;
rep2(i,1,n)
rep2(j,1,m)
{
if(i==1&&j==1)continue;
if(i%2||j%2)f[i][j]=f[i-1][j]+f[i][j-1];
}
cout<<f[n][m];
return 0;
}
2,网络分析
题意:给n个节点和m个操作,建网络是个逐渐的过程,若输入为1,则建立a和b的边,为2,则从节点p开始,先网络中每个与p连通的节点+信息值b;最后逐一输出每个节点的信息值;
本来是想着用bfs去循环整个已形成的图,每个节点加b,但是发现邻接表的真正使用;它其实只可以存储当前头节点下的子结点,可以理解只有一层;
(当然题意是无向图,只有边)这里我想从1开始,整个遍历图是只靠h[1]办不到的;
h[1]只能到2,而h[2]能到3,4所有就需要把每一层的头结点存下来,也就是bfs,所以邻接表的遍历又一次加深了理解;
然后就是加b操作,因为可能会有头结点时另一结点的子结点或者重复遍历到了,加b只能一次,所以需要判断是否遍历过;
#include<bits/stdc++.h>
#define rep1(i,a,n) for(register ll i=a;i<n;i++)
#define rep2(i,a,n) for(register ll i=a;i<=n;i++)
#define per1(i,n,a) for(register ll i=n;i>a;i--)
#define per2(i,n,a) for(register ll i=n;i>=a;i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define INF 0x3f3f3f3f
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
int n,m,k;
const int N=1e4+10;
int e[N],ne[N],h[N],w[N],idx;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int q[N];
int hh,tt;
unordered_map<int,int>appear;
void bfs(int a,int b)
{
hh=1;
unordered_map<int,int>hs;
while(hh<=tt)
{
int t=q[hh++];
//cout<<"t:"<<t<<endl;
if(hs[t]==0)w[t]+=b;
hs[t]=1;
// cout<<"+:"<<t<<" "<<b<<endl;
for(int i=h[t];i!=-1;i=ne[i])
{
if(!hs[e[i]])
{
w[e[i]]+=b;
// cout<<"+:"<<e[i]<<" "<<b<<endl;
hs[e[i]]=1;
}
}
}
}
signed main()
{
quick_cin();
int n,m;
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int c,a,b;
cin>>c>>a>>b;
if(c==1)
{
if(a==b)continue;
add(a,b);
if(appear[a]==0)q[++tt]=a;
appear[a]=1;
appear[b]=1;
}
else
{
if(appear[a]==0)w[a]+=b;
else bfs(a,b);
}
}
rep2(i,1,n)cout<<w[i]<<" ";
return 0;
}
但是这样写会错,也不太明白哪一步错了;
正确解法是并查集的做法:
操作1:加一条边,这个对于并查集来说是基础操作;
操作2:给连通块的每一个点加b:
对于这个操作,我们认为遍历连通块,逐个加b很浪费时间(tle),考虑给根结点加b(连通块在并查集中的表现形式是树)来代表给整个连通块都加b,那么是否可行呢?答案是可行的;我们只需要考虑每个点到根节点的路径权值之和,因为是考虑了根节点,也就考虑了之前的加b操作,所以可行;
但是对于合并两个连通块来说,是不能直接合并的 ;
直接把+5连向+4,会导致+5及其子结点都加4,这是不允许的;
有两种解决方法:
一:新建一个根节点,左右孩子为这两个子树;
二:+5改为+1,再连向+4;这样也合法;
#include<bits/stdc++.h>
#define rep1(i,a,n) for(register ll i=a;i<n;i++)
#define rep2(i,a,n) for(register ll i=a;i<=n;i++)
#define per1(i,n,a) for(register ll i=n;i>a;i--)
#define per2(i,n,a) for(register ll i=n;i>=a;i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define INF 0x3f3f3f3f
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
int n,m,k;
const int N=1e4+10;
int p[N],d[N];
int find(int x)
{
if(p[x]==x||p[x]==p[p[x]])return p[x];
int r=find(p[x]);
d[x]+=d[p[x]];
p[x]=r;
return r;
}
signed main()
{
quick_cin();
int n,m;
cin>>n>>m;
rep2(i,1,n)p[i]=i;
while(m--)
{
int t,a,b;
cin>>t>>a>>b;
if(t==1)
{
a=find(a),b=find(b);
if(a!=b)
{
d[a]-=d[b];
p[a]=b;
}
}
else
{
a=find(a);
d[a]+=b;
}
}
rep2(i,1,n)
{
if(i==find(i))cout<<d[i]<<" ";
else cout<<d[i]+d[find(i)]<<" ";
}
return 0;
}