洛谷P2015
【题意】:
有一棵苹果树,如果树枝有分叉,一定是分
叉(就是说没有只有
个儿子的结点,特别的,这样的二叉树即完满二叉树)。
这棵树共有 个结点(叶子点或者树枝分叉点),编号为 ,树根编号一定是 。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有
个树枝的树。
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。
【输入格式】: 第
行
个数,
和
。
表示树的结点数, 表示要保留的树枝数量。接下来 行描述树枝的信息。
每行 个整数,前两个是它连接的结点的编号。第 个数是这根树枝上苹果的数量。
每根树枝上的苹果不超过
个。
【思路】: 树形dp
的模板题。
根据输入,我们可以很简单的求出每个点 的左二子 和右儿子 。
记 表示考虑到点 最多保留 条边时的答案。我们把边权下放至儿子点上,所以把边权转化为了点权,记 为 的点权。
我们可以得到如下的转移方程:
换言之,就是把依次考虑左右儿子可以保留多少个边。
【代码】:
int f[110][110],n,q;
int ls[110],rs[110];
int a[110][110],val[110];
int dp(int u,int s){
// printf("dp(%d,%d)\n",u,s);
if (s==0) return 0;//注意必须先写这句话
if (!ls[u]&&!rs[u])
return val[u];
if (f[u][s]!=-1)
return f[u][s];//记忆化搜索
f[u][s]=0;
for(int i=0;i<s;i++)
f[u][s]=max(f[u][s],dp(ls[u],i)+dp(rs[u],s-i-1)+val[u]);
return f[u][s];
}
void get_children(int u){
for(int i=1;i<=n;i++)
if (a[u][i]!=-1){
val[i]=a[u][i];ls[u]=i;
a[u][i]=a[i][u]=-1;
get_children(i);
break;
}
for(int i=1;i<=n;i++)
if (a[u][i]!=-1){
val[i]=a[u][i];rs[u]=i;
a[u][i]=a[i][u]=-1;
get_children(i);
break;
}
}
int main(){
freopen("t1.in","r",stdin);
memset(f,-1,sizeof(f));
memset(a,-1,sizeof(a));
scanf("%d%d",&n,&q);
for(int i=1;i<n;i++){
register int u,v;
scanf("%d%d",&u,&v);
scanf("%d",&a[u][v]);
a[v][u]=a[u][v];
}
get_children(1);
// for(int i=1;i<=n;i++){
// printf("ls[%d]=%d,",i,ls[i]);
// printf("rs[%d]=%d\n",i,rs[i]);
// }
cout<<dp(1,q+1);
return 0;
}
洛谷P5542
【题意】: 农夫约翰不擅长多任务处理。他经常分心,很难完成长的项目。目前,他正试图在谷仓的一侧上漆,但他一直在画小矩形区域,然后由于照料奶牛的需要而偏离了方向,使得谷仓的某些部分上漆的涂料比其他部分多。
我们可以将谷仓的一侧描述为一个二维x-y平面
,农夫约翰在该平面上绘制
个矩形,每个矩形的边都与坐标轴平行,每个矩形由谷仓的左下角和右上角点的坐标描述。
农夫约翰想在谷仓上涂几层油漆,这样在不久的将来就不需要再重新粉刷了。但是,他不想浪费时间涂太多的油漆。结果表明,
涂层是最佳用量。请在他画完所有的长方形后,帮他确定谷仓有多少面积被
层油漆覆盖。
【数据范围】:
。
【思路】: 如此恐怖的数据肯定不可以暴力啦……
我们考虑差分。当然,这题肯定是二维差分。
与一维差分类似,我们直接在这里给出转移式。
for(int i=a;i<=c;i++)
for(int j=b;j<=d;j++)
a[i][j]++;//暴力写法
-----------------------------
vis[a+1][b+1]++;
vis[c+1][d+1]++;
vis[a+1][d+1]--;
vis[c+1][b+1]--;
//差分写法
当然,光有差分是不行的,肯定有二维前缀和。同理,我们直接给出代码。
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
vis[i][j]+=vis[i-1][j]+vis[i][j-1]-vis[i-1][j-1];
其实,理解差分和前缀和的方法是,在纸上画出转移的情况,而不是凭空想象。在此,笔者十分想画出图,只是用电脑真的不好画,怕画出来反而妨碍读者理解。
【代码】:
int vis[1010][1010];
int ans,n,k,a,b,c,d;
#define gc getchar()
#define g(c) isdigit(c)
int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
int main(){
n=read();k=read();
for(int i=1;i<=n;i++){
a=read();b=read();
c=read();d=read();
vis[a+1][b+1]++;
vis[c+1][d+1]++;
vis[a+1][d+1]--;
vis[c+1][b+1]--;
}
for(int i=1;i<1001;i++)
for(int j=1;j<1001;j++){
vis[i][j]+=vis[i-1][j]+vis[i][j-1]-vis[i-1][j-1];
if (vis[i][j]==k) ans++;
}
printf("%d",ans);
return 0;
}