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 <bits/stdc++.h>
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 <bits/stdc++.h>
#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 <bits/stdc++.h>
#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 <bits/stdc++.h>
#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;
}