树形动归 专题
直覺上覺得顏色數量不會很多。我就假定最后最多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
總結
- 上一篇: 第六章 BitArray类
- 下一篇: 用Java实现HTTP断点续传功能(ZT