虽然已经很困了
但还是想起标题没取 就来写一下今天的鲜花
话说最近天气真热啊
好多时候都不想出去玩qwq
今天想到了一个很好玩的标题《由于找不到暑假工 所以只好来打休闲ACM》
休闲感觉称不上 但一定会感觉时间过的很快
好像没咋学 就放假了 没咋学 就开学了
AcWing 1073. 树的中心
树网的核 启发 直接找直接枚举每一个点即可 因为我们找直径时就维护了一个前缀和数组 直接枚举
O(n)
一些树的直径的理解:我们树是一个散出去的分支
我们只要知道他的最后一个尾巴(max 是谁那我们通过fa就可以求出直径了 那个尾巴就是我们不断维护的k
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10;
const int M = 1<<12;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int n,h[N],w[N<<1],ne[N<<1],e[N<<1],idx,fa[N<<1],dis[N<<1],k;
void add(int a,int b,int c){
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int f,int x){
fa[x]=f;
if(dis[x]>dis[k])k=x;
for(int i=h[x];~i;i=ne[i]){
int j=e[i];
if(j==f)continue;
dis[j]=dis[x]+w[i];
dfs(x,j);
}
}
signed main(){
fast
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b,c;cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
dis[1]=0,dfs(0,1);
dis[k]=0,dfs(0,k);
int ans=2e9;
for(int i=k;i;i=fa[i])ans=min(ans,max(dis[k]-dis[i],dis[i]));
cout<<ans<<endl;
return ~~(0^_^0);
}
作者:Azusamitsusa
链接:https://www.acwing.com/solution/content/128394/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
然后这个代码我简化到只有29行发了一篇超短题解hh
#include <bits/stdc++.h>
const int N = 1e4+10;
int n,h[N],w[N<<1],ne[N<<1],e[N<<1],idx,fa[N<<1],dis[N<<1],k,ans=2e9;
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int f,int x){
fa[x]=f;
if(dis[x]>dis[k])k=x;
for(int i=h[x];~i;i=ne[i]){
int j=e[i];
if(j==f)continue;
dis[j]=dis[x]+w[i];
dfs(x,j);
}
}
signed main(){
std::cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b,c;std::cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
dis[1]=0,dfs(0,1);
dis[k]=0,dfs(0,k);
for(int i=k;i;i=fa[i])ans=std::min(ans,std::max(dis[k]-dis[i],dis[i]));
std::cout<<ans<<'\n';
return ~~(0^_^0);
}
作者:Azusamitsusa
链接:https://www.acwing.com/solution/content/128394/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
AcWing 1072. 树的最长路径
想到了贪心 可是没看到有负权边
其实正解也是一个贪心 但是暴力遍历了所有点的那种情况
并且维护了一个max值和一个次max值
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10;
const int M = 1<<12;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int n,h[N],e[N<<1],ne[N<<1],w[N<<1],idx,ans=0;
void add(int a,int b,int c){
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
int dfs(int u,int fa){
int d1,d2;
d1=d2=0;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa)continue;
int t=dfs(j,u)+w[i];
if(d1<t)d2=d1,d1=t;
else if(d2<t)d2=t;
}
ans=max(ans,d1+d2);
return d1;
}
signed main(){
fast
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n-1;i++){
int a,b,c;cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
dfs(1,-1);
cout<<ans<<endl;
return ~~(0^_^0);
}
AcWing 1074. 二叉苹果树
我超 我居然没看出来这是一道模板题 呃呃
简直和有依赖的背包问题一模一样啊 就是吧权重从点换成了边
我们分组背包dp时 稍稍改一下就行了
有依赖的背包问题是保留父节点 从而要将父节点从j层删去相当于填满一个m-v【u】的背包
从而我们后面要更新f[u][i]
for(int i=m;i>=v[u];i--)f[u][i]=f[u][i-v[u]]+w[u];
for(int i=0;i<v[u];i++)f[u][i]=0;
而这个我们相当于保留一条连向父节点的边 而这个边是和s相连的
所以在k层必须保留一条边 而j层不用做改动
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2+10;
const int M = 1<<12;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int f[N][N],h[N],e[N<<1],ne[N<<1],w[N<<1],idx,n,m,ans;
void add(int a,int b,int c){
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u,int fa){
for(int i=h[u];~i;i=ne[i]){
int s=e[i];
if(s==fa)continue;
dfs(s,u);
for(int j=m;j>=0;j--){
for(int k=0;k<j;k++){
f[u][j]=max(f[u][j],f[u][j-k-1]+f[s][k]+w[i]);
}
}
}
}
signed main(){
fast
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b,c;cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
dfs(1,-1);
cout<<f[1][m]<<endl;
return ~~(0^_^0);
}
323. 战略游戏
又想贪心了 真蠢 还是没有证明就去写了
感觉自己脑子真的有毛病
后面写的时候又忘了初始化 初始化一定要在最开始 初始化一定要在最开始 初始化一定要在最开始
好在状态转移是对的 那没事了
#include <bits/stdc++.h>
using namespace std;
const int N = 1510;
const int M = 1<<12;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int n,h[N],e[N<<1],ne[N<<1],idx,f[N][N];//f[i][0/1]表示i节点没被选/被选的min
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int dp(int u,int fa){
f[u][0]=0,f[u][1]=1;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa)continue;
dp(j,u);
f[u][0]+=f[j][1];
f[u][1]+=min(f[j][1],f[j][0]);
}
}
signed main(){
while(cin>>n){
memset(h,-1,sizeof h);
idx=0;
for(int i=1;i<=n;i++){
int a,b;scanf("%lld:(%lld)",&a,&b);
while(b--){
int x;cin>>x;
add(a,x),add(x,a);
}
}
memset(f,0,sizeof f);
dp(0,-1);
cout<<min(f[0][0],f[0][1])<<endl;
}
return ~~(0^_^0);
}
1077. 皇宫看守
话说我第一时间居然没看出来这道题和上一道题的区别
后面感性理解了一下 发现区别还是很大
第一下卡在了他的状态转移会和上一层相交 感觉不是那么好做
其实我们还是应该把状态转移方程好好写下来看看再说
还有一个注意的点就是我们f[i][1]的状态转移是要将j的子树都除去的 而不是仅仅的wj
#include <bits/stdc++.h>
using namespace std;
const int N = 1510;
const int M = 1<<12;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int n,h[N],e[N<<1],ne[N<<1],idx,f[N][N],w[N];
bool st[N];
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u){
f[u][2]=w[u];
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
dfs(j);
f[u][0]+=min(f[j][1],f[j][2]);
f[u][2]+=min(min(f[j][1],f[j][0]),f[j][2]);
}
f[u][1]=inf;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
f[u][1]=min(f[u][1],f[j][2]+f[u][0]-min(f[j][1],f[j][2]));
}
}
signed main(){
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++){
int a,c;cin>>a>>w[a]>>c;
while(c--){
int x;cin>>x;
add(a,x);
st[x]=true;
}
}
int root=1;
while(st[root])root++;
dfs(root);
cout<<min(f[root][1],f[root][2])<<endl;
return ~~(0^_^0);
}
一些小理解
感觉状态表示如果想的差不多时 其实状态转移方程大概率都是可写的?