今天的生哥hu测,都是思路题,代码不长,对于思维的提升很有帮助
可惜没有全力去做。。。
T1
分析:
不用SG函数的一道博弈
觉得xz的想法非常的好
如果我们想要留下一个点的权值(假设为x)
当且仅当这个点只有一条连边,这是我们就可以把这条边练连的其他点干掉
游戏结束,如果我们不是在这种情况下进行了与x相连的边的删减
这就会导致这个点的主动权到了另一个人手里
因为两人的目的完全不同,所以对手一定会删掉x来逼近目的
所以先手干脆在叶子上操作,一步结束游戏
生哥的解释更为科学一些:
假设说我们有一棵树是这样的:
tip
叶子节点的找法 son[x]=0
如果根节点(1)只有一个儿子,那ta也算是一种叶子结点
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=501;
int n;
struct node{
int x,y,nxt;
};
node way[N<<1];
int v[N];
int st[N],tot=0;
int son[N];
void add(int u,int w)
{
tot++;
way[tot].x=u;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
tot++;
way[tot].x=w;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}
void dfs(int x,int fa,int dep)
{
int i;
for (int i=st[x];i;i=way[i].nxt)
if (way[i].y!=fa)
son[x]++,
dfs(way[i].y,x,dep+1);
}
int main()
{
freopen("y.in","r",stdin);
freopen("y.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&v[i]);
for (int i=1;i<n;i++)
{
int u,w;
scanf("%d%d",&u,&w);
add(u,w);
}
int ans=0;
dfs(1,0,1);
for (int j=1;j<=n;j++)
if (son[j]==0)
ans=max(ans,v[j]); //叶子节点中的最大值
if (son[1]==1) ans=max(ans,v[1]);
printf("%d",ans);
return 0;
}
T2
分析:
好几个人表示是贪心
记得TA爷说过:贪心千万不要证明,证明不出来的
生哥:这个贪心你最好要证明一下
…
在环上的走法,很明显只有3种
如果走法3用了两次
假设第一次我们在左半边分发a个,右半边分发b个
第二次我们在右半边分发c个,右半边分发d个
则左半部分a+c,右半部分b+d
这就导致有一部分是<=k
(因为a+b+c+d=2*k)
那我们还不如带足了k个只分给半边,这样的花费一定 <= 转两圈
所以3
操作只能使用一次
同时显然,在转一圈的过程中,我们尽量给距离0远的点分发
这样我们分发的一定是一个序列
这样我们就可以枚举3操作时分发的区间
剩下的区间可以贪心求处答案
统计一个最小值就可以了
我们把一个环通过0拆成一条直线
f[i]表示发到i的最小花费
T3
分析:
二分答案
二分是一个很神奇的操作
就相当于是一个水平线
把所有数字分成了大于ta和小于ta两部分
我们把这两部分用01编号
这样原序列就变成了一个01序列
接下来就可以dp了
f[i][j]表示第i位是j需要多少1
三位三位的转移