2020.10.16 洛谷刷题总结

P2016 战略游戏
P2730 [USACO3.2]魔板 Magic Squares
P1379 八数码难题
P3393 逃离僵尸岛

1.战略游戏
最小权覆盖集问题
树形dp:思路:
1.递归搜索预处理
2.回溯DP求最值

主要决策就是选与不选,所以可以设定状态&方程
f[i][0]为:当不在i号节点放士兵时,以i为根的子树上最少需要设置的士兵数
f[i][1]为:当i号节点放士兵时,以i为根的子树上最少需要设置的士兵数
转移方程:
x为父亲节点,y为儿子节点
f[x][0]+=f[y][1];
f[x][1]+=min(f[y][0],f[y][1]);

1.预处理设定一个根(根的选取不影响结果,但是要注意儿子和父亲关系的对应,详见代码),从根节点递归,枚举儿子节点继续递归直到当前节点为叶子节点时开始回溯

2.回溯过程中dp,列转移方程,完事
最后输出根节点选与不选的最小值即可

#include 
using namespace std;
int n,fa[1505],f[1505][2];
vector<int> son[1505];
int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-')f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void find(int x)
{
	for (int i=0;i<son[x].size();i++)
	{
		int y=son[x][i];
		find(y);
		f[x][0]+=f[y][1];
		f[x][1]+=min(f[y][0],f[y][1]);
	}
}
int main()
{
	n=read();
	for (int i=0;i<n;i++)
	{
		int node1=read(),sum=read();
		for (int j=1;j<=sum;j++)//严格对应父子关系
		{
			int node2=read();
			if (fa[node2]==node2||fa[node2])
			{
				son[node2].push_back(node1);
				fa[node1]=node2;
			}
			else
			{
				son[node1].push_back(node2);
				fa[node2]=node1;
			}
		}
		f[i][1]=1;
	}
	find(0);
	printf("%d",min(f[0][1],f[0][0]));
	return 0;
}

2.魔板 Magic Squares
3.八数码难题
同类型的map宽搜题型,主要思路:
将一个二维的状态进行运算转移成一个数字(魔板最多八位,九宫格最多9位,开long long),然后将这个数字映射->对应情况的答案
在宽搜过程中使用临时的一个数组(从队列中取出状态后,将其逆运算转换回数字形态,并在数组上更新状态,如:与四周某个方块交换位置),和一个临时的状态(在数组更新状态后,又重新转换为数字的形式,进行map中的count查找排重,选最优解),用该状态排重,如果在之前没有遍历过,将该种状态map映射答案,并加入队列进行下一步拓展更新,最后只要找到结束状态,输出并结束程序

思想:状态转换,map
完事

魔板 Magic Squares

#include 
#define ll long long
using namespace std;
queue<ll> q;
ll a[10][10],n,nn; 
map<ll,string> m;
void f(char c)
{
	if (m.count(nn)==0)
	{
		m[nn]=m[n]+c;
		q.push(nn);
	}
}
int main()
{
	ll fn=0;
	for (ll i=1;i<=2;i++)
	{
		ll x1,x2,x3,x4;
		if (i==1)
			scanf("%lld %lld %lld %lld",&x1,&x2,&x3,&x4);
		else
			scanf("%lld %lld %lld %lld",&x4,&x3,&x2,&x1);
		fn=fn*10+x1;
		fn=fn*10+x2;
		fn=fn*10+x3;
		fn=fn*10+x4;
	}
	q.push(12348765);
	while (!q.empty())
	{
		n=q.front(),nn=n;
		q.pop();
		if (n==fn)
		{
			string s=m[n];
			ll len=s.length();
			printf("%lld\n",len);
			for (ll i=0;i<len;i++)
				printf("%c",s[i]);
			return 0;
		}
		for (ll i=2;i>=1;i--)
			for (ll j=4;j>=1;j--)
				a[i][j]=nn%10,nn/=10;
		//A
		nn=0;
		for (ll i=2;i>=1;i--)
			for (ll j=1;j<=4;j++)
				nn=nn*10+a[i][j];
		f('A');
		
		//B
		nn=0;
		a[1][0]=a[1][4];
		a[2][0]=a[2][4];
		for (ll i=1;i<=2;i++)
			for (ll j=0;j<=3;j++)
				nn=nn*10+a[i][j];
		f('B');
		
		//C
		nn=0;
		swap(a[1][2],a[1][3]);
		swap(a[1][2],a[2][3]);
		swap(a[1][2],a[2][2]);
		for (ll i=1;i<=2;i++)
			for (ll j=1;j<=4;j++)
				nn=nn*10+a[i][j];
		f('C');
		swap(a[1][2],a[1][3]);
		swap(a[1][3],a[2][3]);
		swap(a[2][2],a[2][3]);
	}
	return 0;
}

八数码难题

#include 
#define ll long long
using namespace std;
queue<ll> q;
int fx[4][2]={{0,-1},{0,1},{-1,0},{1,0}};
ll n,qx,qy,a[5][5];
map<ll,ll> m;
int main()
{
	scanf("%lld",&n);
	if (n==123804765)
	{
		printf("%lld",0);
		return 0;
	}
	q.push(n);
	m[n]=0;
	while (!q.empty())
	{
		n=q.front();
		q.pop();
		ll nn=n;
		if (n==123804765)
		{
			printf("%lld",m[n]);
			return 0;
		}
		for (int i=3;i>=1;i--)
			for (int j=3;j>=1;j--)
			{
				a[i][j]=nn%10,nn/=10;
				if (a[i][j]==0)
					qx=i,qy=j;
			}
		for (int i=0;i<4;i++)
		{
			nn=0;
			int xx=qx+fx[i][0],yy=qy+fx[i][1];
			if (xx>=1&&xx<=3&&yy>=1&&yy<=3)
			{
				swap(a[qx][qy],a[xx][yy]);
				for (int i=1;i<=3;i++)
					for (int j=1;j<=3;j++)
						nn=nn*10+a[i][j];
				if (m.count(nn)==0)
				{
					m[nn]=m[n]+1;
					q.push(nn);
				}
				swap(a[qx][qy],a[xx][yy]);
			}
		}
	}
	return 0;
}

4.逃离僵尸岛
思路:bfs+最短路
1.先将每个僵尸城加入到队列中,bfs更新图上的信息,找出安全城市和危险城市,更新其价格,主要标记是否已经遍历,小优化:当扩展点的距离已经超过了危险范围,可以将改点不入队列
2.开始愉快的dijsktra,注意将边权转换为点权,起点终点权值为0
完事

#include 
#define ll long long
using namespace std;
const ll inf=1e18;
const int maxn=1e5+5;
struct edge
{
	int v,next;
}a[4*maxn];
struct dij
{
	int num;
	ll len;
	friend bool operator < (dij x,dij y)//优先队列优化
	{
		return x.len>y.len;
	}
};
priority_queue<dij> q;//最短路更新队列
queue<int> qq;//bfs队列
ll dis[maxn],w[maxn],p,Q;//w点权值 dis最短距离
int cnt,n,m,k,s,c[maxn],stp[maxn],head[maxn];//c:僵尸城(可以省略),stp:bfs距离
int dgr[maxn];//1僵尸  2危险  0安全 
bool bj[maxn],bjj[maxn];//bj:bfs标记  bjj:dijkstra标记
int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-')f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void add(int u,int v)
{
	cnt++;
	a[cnt].v=v;
	a[cnt].next=head[u];
	head[u]=cnt;
}
void bfs()
{
	while (!qq.empty())
	{
		int u=qq.front();
		qq.pop();
		for (int i=head[u];i;i=a[i].next)
		{
			int v=a[i].v;
			if (bj[v]==1||dgr[v]==1)
				continue;
			bj[v]=1;
			stp[v]=stp[u]+1;
			if (stp[v]<=s)
			{
				qq.push(v);
				dgr[v]=2;
			}
		}
	}
}
void benew()
{
	for (int i=2;i<n;i++)
	{
		if (dgr[i]==0)
			w[i]=(ll)p;
		else if (dgr[i]==2)
			w[i]=(ll)Q;
	}
}
void dijkstra()
{
	for (int i=2;i<=n;i++)
		dis[i]=inf;
	q.push(dij{1,0});
	while (!q.empty())
	{
		dij x=q.top();
		q.pop();
		int u=x.num;
		if (bjj[u]==1)
			continue;
		bjj[u]=1;
		for (int i=head[u];i;i=a[i].next)
		{
			int v=a[i].v;
			if (dgr[v]==1)
				continue;
			ll ww=w[v];
			if (dis[v]>dis[u]+ww)
			{
				dis[v]=dis[u]+ww;
				q.push(dij{v,dis[v]});
			}
		}
	}
}
int main()
{
	n=read(),m=read(),k=read(),s=read();
	p=read(),Q=read();
	for (int i=1;i<=k;i++)
	{
		c[i]=read();
		dgr[c[i]]=1;	
		qq.push(c[i]);
	}
	for (int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		add(x,y);
		add(y,x);
	}
	bfs();
	benew();
	dijkstra();
	printf("%lld",dis[n]);
	return 0;
}

你可能感兴趣的:(图论,树形dp,bfs,队列,算法,图论,bfs,动态规划)