日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

树形动归 专题

發布時間:2025/4/16 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 树形动归 专题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

直覺上覺得顏色數量不會很多。我就假定最后最多10種顏色。

假定一個root。

f[i][k]表示i為root的子樹里i取k色,得到的最小值。

代碼 #include<iostream>
#include
<cstdio>
#define INF 1000000000
using namespace std;
struct node{
int y,next;
}E[
100001];


int cnt,edg[50001],n,x,y,ans,f[50001][11],NN;
void add_edge(int x,int y)
{
++cnt;E[cnt].y=y;E[cnt].next=edg[x];edg[x]=cnt;
}
int dp(int i,int x,int last)
{
if (f[i][x]!=-1) return f[i][x];

int Min=INF,tt;
tt
=x;
for (int j=edg[i];j!=0;j=E[j].next)
if (E[j].y!=last)
{
Min
=INF;
for (int k=1;k<=NN;++k) if (k!=x)
if (dp(E[j].y,k,i)<Min) Min=dp(E[j].y,k,i);
tt
+=Min;
}

f[i][x]
=tt;
return tt;
}
int main()
{
freopen(
"GEMS.in","r",stdin);
freopen(
"GEMS.out","w",stdout);
NN
=10;
scanf(
"%d",&n);
for (int i=1;i<n;++i)
{
scanf(
"%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}
for (int i=1;i<=n;++i) for (int j=1;j<=100;++j) f[i][j]=-1;
ans
=INF;
for (int i=1;i<=NN;++i)
if (dp(1,i,0)<ans) ans=dp(1,i,0);
cout
<<ans<<endl;
return 0;
}

數據生成器

【題目背景】

小明在做NOI2003練習賽的《幸福的老鼠》時覺得題目太簡單了,于是對原題做了一些擴展:

l???????將原題的N從20擴展到200000。

l???????將原題經過一條街道需要的時間改為Ti(1 £ Ti £ 1000000000)分鐘(i為街道的編號)。

l???????增加了一個條件:小狗家Y離老鼠家X的距離小于等于大狗家Z離老鼠家X的距離。

即使這樣,他仍然很快地做了出來。于是,小明打算做一些輸入文件來測試他的程序。現在他已經生成了一些符合題意的圖,不過為了增大測試數據的難度,他希望你能幫他選取一組X、Y、Z,使老鼠拿到禮物的時間盡可能地大。

【小明擴展的題目(注意,你并不需要解決此題)】

幸福的老鼠Jerry要過生日了,小狗大狗分別送了它一份生日禮物。現在Jerry打算從自己家X出發,先到小狗家Y(因為小狗家Y離老鼠家X的距離小于等于大狗家Z離老鼠家X的距離),再到大狗家Z,將兩份禮物取回。

卡通城由N(3 £ N £ 200000)個居住點和N-1條連接居住點的雙向街道組成,經過第i條街道需花費Ti(1 £ Ti £1000000000)分鐘的時間。可以保證,任兩個居住點間都存在通路

不妨設Jerry家在點X,小狗家在點Y,大狗家在點Z。現在,請你計算,Jerry最快需要耗費多長時間才能拿到生日禮物?

【任務描述】

定義:令|AB|表示卡通城中從A點走到B點需要的最少時間。

給出卡通城的地圖,找到一組X、Y、Z,使得:?

l???????|XY|≤|XZ|

l???????|XY|+|YZ|最大。

并求出此時|XY|+|YZ|的值。

【輸入文件】

輸入文件jerrygen.in第一行是兩個整數N(3 £ N £ 200000)和M(M=N-1),分別表示居住點總數和街道總數。從第2行開始到第N行,每行給出一條街道的信息。第i+1行包含整數Ui、Vi、Ti(1£Ui, Vi £ N,1 £ Ti £ 1000000000),表示街道i連接居住點Ui和Vi,并且經過街道i需花費Ti分鐘。

【輸出文件】

輸出文件jerrygen.out僅包含一個整數T,即|XY|+|YZ|的最大值。

【樣例輸入】

4 3

1 2 1

2 3 1

3 4 1

【樣例輸出】

4

先假定一個root。

分析一下實質上是求前三長路。fir[i],sec[i],thi[i]分別表示該樹根子數下的k短。

from[i]在記錄一下fir[i]是用那條路更新的。最后再dfs吧父親也給考慮下去。

有很多這樣先假定樹根,少考慮父親然后最后再線性dfs補充烤爐的題目。

代碼 #include<iostream>
#include
<cstdio>
#define LL long long
#define N 200001
using namespace std;
struct node{
LL y,z,next;
}E[N
*2];

LL edg[N],cnt,x,y,z,ans,fir[N],sec[N],thi[N],from[N],vis[N],n,m;
void add_edge(LL x,LL y,LL z)
{
++cnt;E[cnt].y=y;E[cnt].next=edg[x];edg[x]=cnt;E[cnt].z=z;
}
void dp(LL x,LL last,LL tot)
{
// if (vis[x]) return;
// vis[x]=1;
// ar[x]=tot;
LL v;
for (LL j=edg[x];j!=0;j=E[j].next)
if (last!=E[j].y)
{
dp(E[j].y,x,tot
+E[j].z);
v
=E[j].y;
if (fir[v]+E[j].z>fir[x]) {from[x]=v;thi[x]=sec[x];sec[x]=fir[x];fir[x]=fir[v]+E[j].z;}
else
if (fir[v]+E[j].z>sec[x]) {thi[x]=sec[x];sec[x]=fir[v]+E[j].z;}
else
if (fir[v]+E[j].z>thi[x]) {thi[x]=fir[v]+E[j].z;}
}
}
void dfs(LL x,LL last,LL tot)
{
if (tot>fir[x]) {from[x]=last;thi[x]=sec[x];sec[x]=fir[x];fir[x]=tot;}
else
if (tot>sec[x]) {thi[x]=sec[x];sec[x]=tot;}
else
if (tot>thi[x]) {thi[x]=tot;}
LL kk
=0;
for (LL j=edg[x];j!=0;j=E[j].next)
if (last!=E[j].y)
{
kk
=tot;
if (from[x]!=E[j].y && fir[x]>kk) kk=fir[x];
if (from[x]==E[j].y && sec[x]>kk) kk=sec[x];
dfs(E[j].y,x,kk
+E[j].z);
}

}
int main()
{
freopen(
"jerrygen.in","r",stdin);
freopen(
"jerrygen.out","w",stdout);
scanf(
"%d%d",&n,&m);
for (LL i=1;i<=m;++i)
{
scanf(
"%d%d%d",&x,&y,&z);
add_edge(x,y,z);
add_edge(y,x,z);
}
dp(
1,0,0);
dfs(
1,0,0);
// for (LL i=1;i<=n;++i)
// printf("%d %d %d %d\n",i,fir[i],sec[i],thi[i]);
/*
for (LL i=1;i<=n;++i)
{
if (ar[i]>fir[i]) {thi[i]=sec[i];sec[i]=fir[i];fir[i]=ar[i];}
else
if (ar[i]>sec[i]) {thi[i]=sec[i];sec[i]=ar[i];}
else
if (ar[i]>thi[i]) {thi[i]=ar[i];}
}
*/
// for (LL i=1;i<=n;++i)
// printf("*(%d %d %d %d\n",i,fir[i],sec[i],thi[i]);
for (LL i=1;i<=n;++i)
if (fir[i]+sec[i]*2+thi[i]>ans) ans=fir[i]+sec[i]*2+thi[i];
cout
<<ans<<endl;
return 0;
}

Day 2

(7.18 14:00-19:30)

Tribe council

有一個部族有N個人,這里有一個他們的家族樹,1是根節點表示父親和兒子的關系。節點1是樹根。他們要在神圣的山的西邊進行一個奇特的會議,就坐在一排桌子旁邊,如下圖。每個人都盡可能的往前面的桌子坐,如果那個桌子上當前沒有他的兒子或者父親。

現在讓你確定一個他們就坐的順序,使得他們占的桌子數最多。

任務

求能占有的最多桌子數

輸入文件 tribe.in

第一行是人數N

以下N-1行每行兩個整數x,y,表示x是y的父親

輸出文件tribe.out

把答案輸出到文件

數據范圍

1<N<=100 000

30%的數據N<=1000

每張桌子有無限的容量

樣例

tribe.in

tribe.out

5

1 4

3 2

1 3

3 5

3

解釋

樣例可以以這種順序入座 2 4 1 5 3

  • 2 到桌1
  • 4 到桌1
  • 1 有孩子 (4) 在桌1,所以去桌2
  • 5 到桌1
  • 3 有2個孩子在桌1(2 and 5) 而且有父親 (1)在桌2,所以去桌3.

要求一共能占據多少排,那么就是要使某一個人能坐到盡量靠后的排。在解決問題之前,我們要考慮這樣幾個問題。假如一個人能坐到第n排,那么他必然能坐到1到n排,因為他坐到第n排的時候,他的相鄰點能占據1,2…n-1排,那么讓占據1的先坐,然后到占據2的,……。只要他在適當的時候插入,就能坐到1至n排之中的任意一排。另外,如果一個人能坐到第n排,那么他的相鄰點一定能分別占據1到n-1排,由于這個點之后才到會,所以之前,每個相鄰點都是憑著去掉這個點后的某個子樹的人際關系,坐到一個適當位置的。更確切的說,一個點能坐到第n排,按照一定順序,他的相鄰點p1、p2、p3…能坐到的最后排a1、a2、a3……滿足a1>=1,a2>=2,a3>=3……。所以在得知一個點的所有相鄰點能坐到的最大排后,坐到第一排的肯定用最小的,坐到第二排的肯定用次小的,我們按他們能坐到的最大排來排序,就能很容易的判斷一個點能坐到第幾排。

這題也是要考慮一個點的各個分叉,我們可以先假設樹有一個根,我們先求一個點能憑借它的兒子坐到多少排。然后我們發現,我們沒有考慮了一個點通向根的一個分叉,我們需要把漏掉的補上。假設P是Q的父親節點,P能坐到的最大排已經求出。P要為Q服務,就必須將P的減掉Q這個分支后,再求它坐到的最大排,把P加入Q的相鄰點,由于根節點已經完全考慮,我們從根節點往下,就能把所有點求出。-------集訓隊論文

其實這題跟上題一樣都是先假定樹根,然后再現不考慮父親。

最后再dfs一遍補充考慮父親。

代碼 #include<iostream>
#include
<cstdio>
#define INF 10000000000
#define N 100001
using namespace std;
struct node{
int y,next;
}E[N
*2];

int edg[N],x,y,ans,n;
int sum[N][30];
int f[N],cnt;
void add_edge(int x,int y)
{
++cnt;E[cnt].y=y;E[cnt].next=edg[x];edg[x]=cnt;
}
void dp(int x,int last)
{
for (int j=edg[x];j!=0;j=E[j].next)
if (E[j].y!=last)
{
dp(E[j].y,x);
++sum[x][f[E[j].y ] ];
}
int tot=0;
for (int i=1;i<=20;++i)
{
tot
+=sum[x][i];if (tot>i) tot=i;
}
f[x]
=tot+1;
return;
}
void dfs(int x,int last,int mm)
{

int tot=0;

++sum[x][mm];
for (int i=1;i<=20;++i)
{
tot
+=sum[x][i];if (tot>i) tot=i;
}
if (tot+1>ans) ans=tot+1; // calc_ans


for (int j=edg[x];j!=0;j=E[j].next)
if (E[j].y!=last)
{
--sum[x][ f[E[j].y ] ];

tot
=0;
for (int i=1;i<=20;++i)
{
tot
+=sum[x][i];if (tot>i) tot=i;
}

dfs(E[j].y,x,tot
+1);

++sum[x][ f[E[j].y ] ];

}
}
int main()
{
freopen(
"tribe.in","r",stdin);
freopen(
"tribe.out","w",stdout);
scanf(
"%d",&n);
for (int i=1;i<n;++i)
{
scanf(
"%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}

ans
=0;
dp(
1,0);
for (int i=1;i<=n;++i) if (f[i]>ans) ans=f[i];
dfs(
1,0,0);
cout
<<ans<<endl;
return 0;
}

?? ?選課

[問題描述]

在大學里每個學生,為了達到一定的學分,必須從很多課程里選擇一些課程來學習,在課程里有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有N門功課,每門課有個學分,每門課有一門或沒有直接先修課(若課程a是課程b的先修課即只有學完了課程a,才能學習課程b)。一個學生要從這些課程里選擇M門課程學習,問他能獲得的最大學分是多少?

輸入:

第一行有兩個整數N,M用空格隔開。(1<=N<=300,1<=M<=160)

接下來的N行,第I+1行包含兩個整數ki和si, ki表示第I門課的直接先修課,si表示第I門課的學分。若ki=0表示沒有直接先修課(1<=ki<=N,1<=si<=20)。

輸出:

只有一行,選M門課程的最大得分。

樣例:

輸入:

7? 4

2? 2

0? 1

0? 4

2? 1

7? 1

7? 6

2? 2

輸出:

13

徐持衡論文。。。。。

代碼 #include<iostream>
#include
<cstdio>
#define N 1000
using namespace std;
struct node{
int y,next;
} E[N
*2];

int edg[N],ans,s[N],n,x,C,cnt;
int f[N][N];
void add_edge(int x,int y)
{
++cnt;E[cnt].next=edg[x];edg[x]=cnt;E[cnt].y=y;
}
void dp(int u,int C)
{
if (C<=0) return;
int v;

for (int j=edg[u];j!=0;j=E[j].next)
{
v
=E[j].y;
for (int i=0;i<=C;++i) f[v][i]=f[u][i];
dp(v,C
-1);
for (int i=0;i<=C-1;++i)
if (f[v][i]+s[v]>f[u][i+1]) f[u][i+1]=f[v][i]+s[v];
}
}
int main()
{
freopen(
"choice.in","r",stdin);
freopen(
"choice.out","w",stdout);
scanf(
"%d%d",&n,&C);
for (int i=1;i<=n;++i)
{
scanf(
"%d%d",&x,&s[i]);
add_edge(x,i);
}
dp(
0,C+1);
for (int i=0;i<=C;++i) if (f[0][i]>ans) ans=f[0][i];
cout
<<ans<<endl;
return 0;
}

轉載于:https://www.cnblogs.com/LitIce/archive/2010/11/09/1872835.html

總結

以上是生活随笔為你收集整理的树形动归 专题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。