2017年4月4日考試總結

第一題:最長鏈

題目描述:給定一棵有n個節點的樹,求每個節點到其他節點的最大距離

輸入:第一行是一個自然數n(n10000),接下來 (n1)行描述: 第i行包含兩個自然數, 表示編號爲i的節點連接到的節點編號和這條網線的長度..距離總長不會超過10^9.每行中的兩個數字用空格隔開.

輸出:包含n.i行表示對於離編號爲i的節點最遠的節點與該節點的距離Si(1in).

成績:AC

題解:g[x][0]g[x][1]表示以1爲根的樹中,x子樹中的最長鏈和次長鏈(不能有相同的邊)dp[x][0],dp[x][1]表示整棵樹中過x的最遠距離和次遠距離(也不能有相同的邊)。

(方程式情況難以描述,就不打了23333333

P.s:法二:樹的直徑。

分析:前一天剛講過,還是寫得很快的~\(≧▽≦)/~啦啦啦。

 

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<stack>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
const int N=10000+10;
inline void getint(int&num){
    char c;int flag=1;num=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
    num*=flag;
}
struct bian{
	int v,w,next;
}arr[N<<1];
int n,a,w,cnt,fir[N],g[N][2],dp[N][2],tmp[7];
inline void link(int a,int b,int w){
	arr[++cnt].v=a,arr[cnt].w=w;
	arr[cnt].next=fir[b],fir[b]=cnt;
}
void work(int x,int pre){
	for(int i=fir[x];i;i=arr[i].next)
		if(arr[i].v!=pre){
			work(arr[i].v,x);
			if(g[arr[i].v][0]+arr[i].w>g[x][0])
				g[x][1]=g[x][0],g[x][0]=g[arr[i].v][0]+arr[i].w;
			else if(g[arr[i].v][0]+arr[i].w>g[x][1])
				g[x][1]=g[arr[i].v][0]+arr[i].w;
		}
}
void dfs(int x,int pre,int w){
	tmp[1]=tmp[2]=tmp[3]=tmp[4]=0;
	
	if(dp[pre][0]==g[x][0]+w){
		tmp[1]=g[x][0],tmp[2]=g[x][1];
		tmp[3]=dp[pre][1]+w;
		sort(tmp+1,tmp+4);
		dp[x][0]=tmp[3],dp[x][1]=tmp[2];
	}

	else if(dp[pre][1]==g[x][0]+w){
		tmp[1]=g[x][0],tmp[2]=g[x][1];
		tmp[3]=dp[pre][0]+w;
		sort(tmp+1,tmp+4);
		dp[x][0]=tmp[3],dp[x][1]=tmp[2];
	}
	
	else{
		tmp[1]=g[x][0],tmp[2]=g[x][1];
		tmp[3]=dp[pre][0]+w;
		tmp[4]=dp[pre][1]+w;
		sort(tmp+1,tmp+5);
		dp[x][0]=tmp[4],dp[x][1]=tmp[3];
	}
	
	for(int i=fir[x];i;i=arr[i].next)
		if(arr[i].v!=pre)
			dfs(arr[i].v,x,arr[i].w);
}
int main(){
	getint(n);
	for(int i=2;i<=n;i++){
		getint(a),getint(w);
		link(i,a,w),link(a,i,w);
	}
	work(1,0),dfs(1,0,0);
	for(int i=1;i<=n;i++)
		printf("%d\n",dp[i][0]);
}

第二題:比賽轉播

題目描述:一個電視網絡計劃轉播一場重要的足球比賽。網絡中的傳輸點和接收點(即用戶)可以表示一棵樹。這棵樹的根是一個傳輸點,它將轉播比賽。樹的葉節點是可能要接受這場比賽的用戶(他當然可以選擇不看比賽,這樣就不要交款)。其他非根節點,非葉節點的中間節點爲數據的中轉站。將一個信號從一個傳輸點傳到另一個傳輸點的花費是給定的。整個轉播的費用就是每一個傳輸費用的總和。每一個用戶(葉節點)都準備付一定的錢來看這場比賽。電視網絡公司要決定是否要給這個用戶提供電視信號。例如:給一個節點傳輸信息的花費太大,而他願意的付款也很少時,網絡公司可能選擇不給他轉播比賽。寫一個程序,找到一個傳輸方案使最多的用戶能看到轉播比賽,且轉播的費用不超過所有接收信號用戶的交款。

輸入:輸入文件的第一行包含兩個整數NM(2<=N<=3000,1<=M<=N-1)N,M表示分別表示樹的節點數和想觀看比賽的用戶數。樹的根節點用1表示,中間節點的標號爲2~N-M,用戶的節點標號爲N-M+1~N。接下來的N-M行表示傳輸點的信息(依次是節點12……):K A1 C1 A2 C2 ……Ak Ck 表示一個傳輸點將信號傳給K個用戶,每一個包含兩個數ACA表示傳輸點或用戶的節點號,C表示傳輸的花費。最後一行含有用戶的數據,有M個整數表示他們看這場比賽願意的付費。

輸出格式:僅一行包含一個整數,最大的用戶數。

成績:AC

題解:dp[i][j]表示i子樹中選j戶的最大收益(多叉樹轉二叉樹)

P.s:法二:揹包。

分析:由於揹包法理解得不是很清楚(~@_@~),只好多叉樹轉二叉樹,然而明明昨天還弄了半天沒調出來,今天可能狀態來了吧。。。(2333333333)(qwq。。。。2333333333333333333~\(≧▽≦)/~啦啦啦)

 

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<stack>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
const int N=3000+10;
inline void getint(int&num){
    char c;int flag=1;num=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
    num*=flag;
}
struct bian{
	int v,next;
}arr[N<<1];
struct node{
	int l,r;
}tree[N<<1];
int T,n,m,cnt,a,w,fir[N],A[N],son[N],sum[N],dp[N][N];
bool vis[N][N];
inline void link(int a,int b){
	arr[++cnt].v=a,arr[cnt].next=fir[b],fir[b]=cnt;
}
void Son(int x){
	if(!x)	return ;
	Son(tree[x].l),Son(tree[x].r);
	son[x]=son[tree[x].l]+son[tree[x].r]+(x>n-m);
}
void Sum(int x){
	if(!x)	return ;
	Sum(tree[x].l),Sum(tree[x].r);
	sum[x]=sum[tree[x].l]+sum[tree[x].r]+A[x];
}
void dfs(int x,int k){
	if(vis[x][k])	return ;
	vis[x][k]=1;
	int tmp=-0x7f7f7f7f;
	if(k==son[x])	tmp=sum[x];
	else if(!k)	tmp=0;
	else if(!tree[x].l&&!tree[x].r)	tmp=A[x];
	else if(!tree[x].l){
		if(son[tree[x].r]>=k)
			dfs(tree[x].r,k),tmp=max(tmp,dp[tree[x].r][k]);
		if(son[tree[x].r]>=k-1&&x>n-m)
			dfs(tree[x].r,k-1),tmp=max(tmp,dp[tree[x].r][k-1]+A[x]);
	}
	else if(!tree[x].r){
		if(son[tree[x].l]>=k)
			dfs(tree[x].l,k),tmp=max(tmp,dp[tree[x].l][k]+A[x]);
	}
	else{
		if(son[tree[x].r]>=k)
			dfs(tree[x].r,k),tmp=max(tmp,dp[tree[x].r][k]);
		for(int i=1;i<=k;i++){
			if(son[tree[x].l]<i||son[tree[x].r]<k-i)	continue ;
			dfs(tree[x].l,i),dfs(tree[x].r,k-i);
			tmp=max(tmp,dp[tree[x].l][i]+dp[tree[x].r][k-i]+A[x]);
		}
	}
	dp[x][k]=tmp;
}
int main(){
	getint(n),getint(m);
	for(int i=1;i<=n-m;i++){
		getint(T);
		while(T--){
			getint(a),getint(A[a]);
			link(a,i),A[a]=-A[a];
		}
	}
	for(int i=n-m+1;i<=n;i++)	
		getint(w),A[i]+=w;
	for(int i=1;i<=n;i++){
		tree[i].l=arr[fir[i]].v;
		int now=tree[i].l;
		for(int j=arr[fir[i]].next;j;j=arr[j].next)
			tree[now].r=arr[j].v,now=arr[j].v;
	}
	Son(1),Sum(1);
	for(int i=1;i<=m;i++)
		dfs(1,i);
	for(int i=m;i>=0;i--)
		if(dp[1][i]>=0){
			printf("%d\n",i);return 0;
		}
	putchar(48),putchar(10);
	return 0;
}

第三題:葉子的顏色(color)

題目描述:給一棵m個結點的無根樹,你可以選擇一個度數大於1的結點作爲根,然後給一些結點(根、內部結點和葉子均可)着以黑色或白色。你的着色方案應該保證根結點到每個葉子的簡單路徑上都至少包含一個有色結點(哪怕是這個葉子本身)。

對於每個葉結點u,定義c[u]爲從根結點到u的簡單路徑上最後一個有色結點的顏色。給出每個c[u]的值,設計着色方案,使得着色結點的個數儘量少。

輸入:第一行包含兩個正整數m, n,其中n是葉子的個數,m是結點總數。結點編號爲12,…,m,其中編號12,… ,n是葉子。以下n行每行一個01的整數(0表示黑色,1表示白色),依次爲c[1]c[2],…,c[n]。以下m-1行每行兩個整數ab1<=a < b <= m),表示結點ab有邊相連。

輸出:僅一個數,即着色結點數的最小值。

成績:。。。。。。。。。。(TAT

題解:因爲顯然染色染在越上面,影響的結點越多,答案就越優,所以從根開始向下,他的子樹中黑色多就染黑色,白色多就染白色(一樣都可以用2表示)一邊dfs一邊記錄顏色,

Dp[i]等於兒子dp的和減去子樹中與i同色的個數(包括2)(i染色了他的子樹就不用染了)。

分析:其實第一反應就是差不多的貪心,但是後來不知道爲什麼否掉了((o )!),後來一直在分情況dp(本來分情況dp只需要分當前節點的染色情況就行了,然而我一直在糾結不能與父輩節點染同樣的顏色,就用了一維來表示最近染了色父輩節點的顏色,然後繞着繞着把自己重新繞進去了(o )&(o),不知道自己在搞什麼鬼)

P.s:dp法: dp[i][0]=sigma(min(dp[j][0]-1,dp[j][1],dp[j][2]))+1

dp[i][1]=sigma(min(dp[j][0],dp[j][1]-1,dp[j][2]))+1

dp[i][2]=sigma(min(dp[j][0],dp[j][1],dp[j][2]));

 

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<stack>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
const int N=10000+10;
inline void getint(int&num){
    char c;int flag=1;num=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
    num*=flag;
}
struct bian{
    int v,next;
}arr[N<<1];
int n,m,cnt,a,b,sum[2],dp[N],fir[N],c[N];
bool vis[N];
inline void link(int a,int b){
    arr[++cnt].v=a,arr[cnt].next=fir[b],fir[b]=cnt;
}
void dfs(int x,int pre){
	if(c[x]!=-1)	dp[x]=1;
	else{
		int tot[3];
		tot[0]=tot[1]=tot[2]=0;
		for(int i=fir[x];i;i=arr[i].next)
			if(arr[i].v!=pre)
				dfs(arr[i].v,x),tot[c[arr[i].v]]++,dp[x]+=dp[arr[i].v];
		if(tot[0]==tot[1])	c[x]=2;
		else	c[x]=tot[0]>tot[1]?0:1;
		dp[x]-=max(tot[0],tot[1])+tot[2]-1;
	}
}
int main(){
    getint(n),getint(m);
	for(int i=1;i<=n;i++)	c[i]=-1;
    for(int i=1;i<=m;i++)
        getint(c[i]),sum[c[i]]++;
    for(int i=1;i<n;i++){
        getint(a),getint(b);
        link(a,b),link(b,a);
    }
    if(!sum[0]||!sum[1]){
        printf("1\n");return 0;
    }
	dfs(n,-1),printf("%d\n",dp[n]);
    return 0;
}

總結:一二題很簡單,寫得也挺快的,(~\(≧▽≦)/~啦啦啦)然後第三題時剩的時間也很多,最後深深陷入無法自拔(o)233333,做作業一定要有效率(說不定就撞原題了233-QwQ-)如果一個思路太繞太久沒有頭緒,最好儘快換一個(QnQ&TAT),dp的考試裏也是可以貪心的(2333333),總之第二題過了還是很開心的~\(≧▽≦)/~啦啦啦(忠心祝願這種神奇的狀態多來幾次\(^o^)/~

又及:清明節快樂!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章