1. 程式人生 > 其它 >【解題報告】CF815C

【解題報告】CF815C

【解題報告】CF815C

題目大意

Kellen想要買 \(n\) 件東西,她有 \(b\) 塊錢,實際上,超市也就只有 \(n\) 件東西,超市為Kellen準備了 \(n\) 種優惠券,但是對於除第一件商品以外的優惠券,必須在使用第 \(x_i\) 張優惠券之後方才可以使用,你能幫她瞭解一下在不超過她手上的錢的情況下,她最多能買多少東西

輸入第一行兩個整數,\(n\)\(b\)

從第二行到第 \(n+1\) 行,除第二行前兩個整數之外,後面的每行三個整數 $c_i \space,d_i \space $ 和 \(x_i\)

字母意義皆如題目所示

思路

這道題目,我們可以從必須使用 \(x_i\)

之後才可以使用 \(i\) 知道,這個類似於沒有上司的舞會,或者是選課,因此我們要使用樹形DP

按照之前的慣例,我們可以設定如下狀態

設定 \(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