ybt 1262:【例9.6】挖地雷
洛谷 P2196 [NOIP1996 提高组] 挖地雷
注:以上两题输入格式不同
根据题意,每个地窖是一个顶点,每条路径是一条有向边,每个地窖的地雷数是该顶点的权值(简称点权),这是个有向无环图。
该题可抽象为:求有向无环图上,点权加和最大的路径,可以用动态规划的方法来求解。
顶点编号从小到大,只存在小编号顶点指向大编号的顶点的边,不存在大编号顶点指向小编号顶点的边,说明该顶点按照序号从小到大符合拓扑排序。或者也可以直接对该图进行拓扑排序,在拓扑排序的过程中进行状态转移。
状态定义 dp[i]
:以顶点i为终点的所有路径中,点权加和最大的路径的点权加和。
记第i顶点的地雷数量,也就是点权为a[i]
。
初始状态 顶点i自己,就是一个以顶点i为终点的路径,权值为a[i]
。因此设dp[i] = a[i]
dp[u1]
,加上顶点v的点权a[v]
,即 dp[u1] + a[v]
dp[u2]
,加上顶点v的点权a[v]
,即 dp[u2] + a[v]
dp[v]
为:以 u u u为终点的所有路径的最大点权加和dp[u]
加上顶点v的点权a[v]
,即dp[u] + a[v]
对于拓扑排序序列,如果u到v有路径,那么在拓扑排序序列中u在v前面,因此只需要遍历拓扑排序序列中所有顶点v前面的顶点u,通过dp[v] = max(dp[v], dp[u]+a[v])
更新dp[v]
,即可得到dp[v]
。
可以直接进行拓扑排序,在拓扑排序过程中进行状态转移
顶点u出队时,访问顶点u的邻接点v,顶点u在拓扑排序序列中在v的前面,顶点u出队时dp[u]
的值已经求好了,那么dp[u]+a[v]
就是dp[v]
的一个可能的结果,可以通过dp[v] = max(dp[v], dp[u]+a[v])
更新dp[v]
。
或者先得到拓扑排序的序列,而后遍历拓扑排序序列完成状态转移。如果使用邻接表存图,则需要对原图的边逆向后建图。这样方便取到指向顶点i的所有顶点。
用数组path记录路径,用递归或栈的方法逆序输出。
最后dp数组最大值的下标为mxi,即以顶点mxi为终点的路径的点权加和最大,输出该路径及点权加和。
#include
using namespace std;
#define N 205
int n, a[N], dp[N], deg[N], pre[N], mxi = 1;//dp[i]:终点为i的路径的最大点权加和,挖到第i地窖时最多有多少地雷
vector<int> edge[N];
void topoSort()
{
queue<int> que;
for(int i = 1; i <= n; ++i) if(deg[i] == 0)
{
que.push(i);
dp[i] = a[i];
}
while(!que.empty())
{
int u = que.front();
que.pop();
if(dp[u] > dp[mxi]) //求dp数组最大值下标
mxi = u;
for(int v : edge[u])
{
if(dp[v] < dp[u]+a[v])
{
pre[v] = u;
dp[v] = dp[u]+a[v];
}
if(--deg[v] == 0)
que.push(v);
}
}
}
void show(int i)//输出终点为i的路径
{
if(pre[i] == 0)
{
cout << i;
return;
}
show(pre[i]);
cout << '-' << i;
}
int main()
{
int x, y;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i];
while(cin >> x >> y && !(x == 0 && y == 0))
{
edge[x].push_back(y);
deg[y]++;
}
topoSort();
show(mxi);
cout << endl << dp[mxi];
return 0;
}
#include
using namespace std;
#define N 205
bool edge[N][N];//edge[i][j]:从i到j有一条弧
int dp[N];//dp[i]:以顶点i为终点的所有路径中,路径上地雷数量加和最大的路径的地雷数量加和。
int a[N], path[N];//a[i]:第i个地窖的地雷个数 path[i]:第i顶点的前一个顶点
void showPath(int i)//输出以i为终点的路径
{
if(path[i] == 0)
{
cout << i;
return;
}
showPath(path[i]);
cout << '-' << i;
}
int main()
{
int n, f, t, mxi = 1;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i];
while(cin >> f >> t && !(f == 0 && t == 0))
edge[f][t] = true;
for(int i = 1; i <= n; ++i)
{
dp[i] = a[i];
for(int j = 1; j < i; ++j) if(edge[j][i] && dp[i] < dp[j]+a[i])
{
dp[i] = dp[j]+a[i];
path[i] = j;
}
}
for(int i = 1; i <= n; ++i)
if(dp[i] > dp[mxi])
mxi = i;
showPath(mxi);
cout << endl << dp[mxi];
return 0;
}
#include
using namespace std;
#define N 205
vector<int> edge[N];//edge[i]:所有满足存在弧的j保存在edge[i]中
int dp[N];//dp[i]:以顶点i为终点的所有路径中,路径上地雷数量加和最大的路径的地雷数量加和。
int a[N], path[N];//a[i]:第i个地窖的地雷个数 path[i]:第i顶点的前一个顶点
void showPath(int i)//输出以i为终点的路径
{
if(path[i] == 0)
{
cout << i;
return;
}
showPath(path[i]);
cout << '-' << i;
}
int main()
{
int n, f, t, mxi = 1;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i];
while(cin >> f >> t && f && t)
edge[t].push_back(f);//建与原图弧的方向逆向的图,f到t有一条弧,那么把f加入edge[t]这个vector中。
for(int i = 1; i <= n; ++i)
{
dp[i] = a[i];
for(int j : edge[i]) if(dp[i] < dp[j] + a[i])
{
dp[i] = dp[j]+a[i];
path[i] = j;
}
}
for(int i = 1; i <= n; ++i)
if(dp[i] > dp[mxi])
mxi = i;
showPath(mxi);
cout << endl << dp[mxi];
return 0;
}
#include
using namespace std;
#define N 205
vector<int> edge[N];//edge[i]:所有满足存在弧的j保存在edge[i]中
int dp[N];//dp[i]:以顶点i为终点的所有路径中,路径上地雷数量加和最大的路径的地雷数量加和。
int n, deg[N], a[N], pre[N];//a[i]:第i个地窖的地雷个数 path[i]:第i顶点的前一个顶点
void showPath(int i)//输出以i为终点的路径
{
if(pre[i] == 0)
{
cout << i;
return;
}
showPath(pre[i]);
cout << ' ' << i;
}
void topoSort()
{
queue<int> que;
for(int v = 1; v <= n; ++v)
if(deg[v] == 0)
{
dp[v] = a[v];
que.push(v);
}
while(que.empty() == false)
{
int u = que.front();
que.pop();
for(int v : edge[u])
{
if(dp[v] < dp[u]+a[v])
{
dp[v] = dp[u]+a[v];
pre[v] = u;
}
if(--deg[v] == 0)
que.push(v);
}
}
}
int main()
{
int f, t, mxi = 1;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i];
for(int u = 1; u < n; ++u)
for(int v = u+1; v <= n; ++v)
{
cin >> t;
if(t)
{
edge[u].push_back(v);
deg[v]++;
}
}
topoSort();
for(int i = 1; i <= n; ++i)
if(dp[i] > dp[mxi])
mxi = i;
showPath(mxi);
cout << endl << dp[mxi];
return 0;
}