【解題報告】CF815C
阿新 • • 發佈:2021-10-03
【解題報告】CF815C
題目大意
Kellen想要買 \(n\) 件東西,她有 \(b\) 塊錢,實際上,超市也就只有 \(n\) 件東西,超市為Kellen準備了 \(n\) 種優惠券,但是對於除第一件商品以外的優惠券,必須在使用第 \(x_i\) 張優惠券之後方才可以使用,你能幫她瞭解一下在不超過她手上的錢的情況下,她最多能買多少東西
輸入第一行兩個整數,\(n\) 和 \(b\)
從第二行到第 \(n+1\) 行,除第二行前兩個整數之外,後面的每行三個整數 $c_i \space,d_i \space $ 和 \(x_i\)
字母意義皆如題目所示
思路
這道題目,我們可以從必須使用 \(x_i\)
按照之前的慣例,我們可以設定如下狀態
設定 \(f[i][j][0/1]\) 表示在以 \(i\) 為根的子樹中,選擇 \(j\) 個物品,在第 $i $ 個商品上面是否使用優惠券可以最多購買東西的數量
然後我們根據定義,我們可以列出如下初始狀態
- 第 \(x\) 個節點的子樹不購買,他自己也不購買,不使用優惠券,沒有代價
- 第 \(x\) 個節點的子樹不購買,購買他自己,但是也不使用優惠券,代價是買 \(x\) 花的錢 \(c_x\)
- 第 \(x\) 個節點地字數不購買,購買他自己,使用優惠券,這樣他們的代價就是 \(c_x-d_i\)
所以我們的初始狀態就定義完了
然後想動態轉移方程
怎麼想呢,列舉自己的大小和子樹和他所有兒子的大小
我們可以有
\[f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[son][j][0]) \]這個的意思呢就是x節點有 \(i+j\) 個節點,然後選除了某個子樹外的 \(i\) 個節點的買的數量和買這某個子樹的 \(j\) 個節點的總的可以買的
\[f[x][i+j][1]= min \begin{cases} f[x][i][1]+f[son][j][0] \\ f[x][i][1]+f[son][j][1] \end{cases} \]這個方程的意思和上面的差不多,也就不做過多解釋了
因此我們對著是輸入的資料瘋狂建樹,然後將自己的邊指向需要自己的節點,然後我們跑一邊 dfs 就可以把樹形DP寫完了
程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
const int maxn=5005;
int n,b,ans;
int c[maxn],d[maxn];
int x[maxn];
int size[maxn],f[maxn][maxn][5];
struct edge{
int e,next;
}ed[maxn<<1];
int en,first[maxn];
void add_edge(int s,int e)
{
en++;
ed[en].next=first[s];
first[s]=en;
ed[en].e=e;
}
void dfs(int x)
{
size[x]=1;
f[x][0][0]=0;
f[x][1][0]=c[x];
f[x][1][1]=c[x]-d[x];
for(int i=first[x];i;i=ed[i].next)
{
int e=ed[i].e;
dfs(e);
for(int i=size[x];i>=0;i--)
{
for(int j=0;j<=size[e];j++)
{
f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[e][j][0]);
f[x][i+j][1]=min(f[x][i+j][1],min(f[x][i][1]+f[e][j][0],f[x][i][1]+f[e][j][1]));
}
}
size[x]+=size[e];
}
}
int main()
{
memset(f,0x3f3f3f,sizeof(f));
cin>>n>>b;
cin>>c[1]>>d[1];
for(int i=2;i<=n;i++)
{
cin>>c[i]>>d[i]>>x[i];
add_edge(x[i],i);
}
dfs(1);
for(int i=n;i>=1;i--)
{
if(f[1][i][0]<=b||f[1][i][1]<=b)
{
ans=i;
break;
}
}
cout<<ans<<'\n';
return 0;
}
本博文為wweiyi原創,若想轉載請聯絡作者,qq:2844938982