您的位置:首页 > 其它

Codeforces Round #436 (Div. 2)E,F详解

2017-09-27 00:08 357 查看
鉴于太多人把D做出来了,实际上D就是道模拟简单题,所以本篇博客并没有D的题解。如果有需要的读者可以联系博主,博主会很热心的为你解释的(笑)。。

E题题解:Polycarp家着火了。。他家里有n个值钱的东西,每一个东西有一个营救需要的时间和一个烧毁需要的时间。问Polycarp能救出的东西的最大价值为多少,并输出营救顺序。

思路:裸的01背包问题。。如果不明白01背包问题可以看看紫书的动规专题。

思路出来了,博主觉得有必要讲的两点就是,一,它和01背包问题唯一不同的是有一个限制时间,不像01背包啥时候都可以拿,它拿某个物品的时间是一段区间。二,能拿某个物品的时间怎么算,首先状态转移方程式为

dp[T]=max(dp[T],dp[T-t[i]]+w[i]),显然T< d[i],并且T>t[i].所以只会更新该区间的值,但更新完了肯定会影响后面的T(从这个式子就可以看出),所以为了复杂度低(你也可以更新后面的T,加个判断即可),首先把物品按照d排个序,先取小的,再取大的,这样就可保证再更新大的时候小的已经全部被更新了。

代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct node
{
int t, d, p;
int idx;
bool operator<(const node &b)const
{
return d < b.d;
}
}nodes[205];
vector<int>V[2005];
int dp[2005];
int main()
{
int n;
cin >> n;
for (int i = 1;i <= n;i++)
{
scanf("%d%d%d", &nodes[i].t, &nodes[i].d, &nodes[i].p);
nodes[i].idx = i;
}
sort(nodes + 1, nodes + 1 + n);
for(int i=1;i<=n;i++)
for (int j = nodes[i].d - 1;j >= nodes[i].t;j--)
{
if (dp[j] < dp[j - nodes[i].t] + nodes[i].p)
{
dp[j] = dp[j - nodes[i].t] + nodes[i].p;
V[j] = V[j - nodes[i].t];
V[j].push_back(nodes[i].idx);
}
}
int theans = 0;
int idx=-1;
for(int i=0;i<=2000;i++)
if (theans < dp[i])
{
theans = dp[i];
idx = i;
}
cout << theans << endl;
if (idx == -1)
{
cout << 0 << endl;
return 0;
}
int Size = V[idx].size();
cout << Size << endl;
for (int i = 0;i < Size;i++)
{
if (i != 0)printf(" ");
printf("%d", V[idx][i]);
}
return 0;
}


F题题意:给你一个图,有q次询问,从s到t的结点最小字典序路径中的第k个结点是谁。

思路:这题好啊,让博主学习到了图上的倍增。另外也让博主领悟到了一个小套路,即如果题目给你一个k,让你求路径上的第k个点,这大概就需要用倍增的思想了。

思路挺简单的,那么如何用倍增呢?请读者仔细看代码,并理解从中倍增的奥妙和如何用倍增判断是环的。

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 3005;
const int maxq = 4e5 + 10;
vector<int>G[maxn], TO[maxn];
vector < pair<pair<int, int>, int>>Q[maxq];
bool vis[maxn];
#define MP(x,y) make_pair(x,y)

void dfs(int node)
{
if (vis[node])return;
vis[node] = 1;
for (int i : TO[node])
dfs(i);
}
int st[maxn][13];
int ans[maxq];
int main()
{
int n, m, q;
cin >> n >> m >> q;
int u, v;
for (int i = 1;i <= m;i++)
{
scanf("%d%d", &u, &v);
G[u].push_back(v);
TO[v].push_back(u);
}
int s, t, k;
for (int i = 1;i <= q;i++)
{
scanf("%d%d%d", &s, &t, &k);
Q[t].push_back(MP(MP(s, k), i));
}
for (int i = 1;i <= n;i++)sort(G[i].begin(), G[i].end());
for (int i = 1;i <= n;i++)if(Q[i].size())
{
memset(vis, 0, sizeof(vis));
dfs(i);
memset(st, 0, sizeof(st));
for (int j = 0;j < 13;j++)st[n + 1][j] = n + 1;
for (int j = 1;j <= n;j++)
{
if (j == i)st[j][0] = n + 1;
else if (vis[j])
{
for (int v : G[j])
{
if (!vis[v])continue;
st[j][0] = v;
break;
}
}
}
for (int j = 1;j < 13;j++)
for (int u = 1;u <= n;u++)if(vis[u])
st[u][j] = st[st[u][j - 1]][j - 1];
for (auto it : Q[i])
{
int s = it.first.first;
int k = it.first.second - 1;
int id = it.second;
if (vis[s])
{
if (st[s][12] == n + 1)
{
for (int j = 0;j < 13;j++)
if (k&(1 << j))
s = st[s][j];
if (s != n + 1)
ans[id] = s;
else
ans[id] = -1;
}
else
ans[id] = -1;
}
else
{
ans[id] = -1;
}

}
}

for (int i = 1;i <= q;i++)
cout << ans[i] << endl;
return 0;

}


代码浑然天成啊。。。博主想解释为啥用离线,但感觉没必要,因为都这样写了,还能用在线不成。希望读者仔细阅读代码,学会了又有新姿势上分了~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: