UVA-1220-Party at Hali-Bula && UVA-1218-Perfect Service(樹形DP)
阿新 • • 發佈:2018-11-03
UVA-1220-Party at Hali-Bula
題意:
一個公司員工要舉行聚會,要求任意一個人不能和他的直接上司同時到場,一個員工只有一個支系上司,現在求最多有多少人到場,並且方案是否唯一(紫書282頁)
分析:
紫薯寫的很清楚,而且也很基礎,就不重複了,只做幾點記錄和總結
- 輸入中輸入的是名字,每個名字要和一個id對應,當然最容易想到的就是map。但是還需要注意一點,就是不能保證輸入的順序,也就是說如果首先輸入的父節點之前沒有出現過,那麼就沒辦法獲取到父節點的id。建樹時要考慮輸入資料的順序(或者是重複輸入的)
- 關於此題,因為要判斷唯一性,所以不是單純的樹的最大獨立點集問題。而新增的陣列
f
#define maxn 210 vector<int> v[maxn]; map<string,int> M; int n; int ans,res; int dp[maxn][2],f[maxn][2]; void dfs(int u) { dp[u][0] = 0; dp[u][1] = 1; f[u][0] = f[u][1] = 1; int l = v[u].size(); //樹形dp基本框架 for(int i=0;i<l;i++) { int y = v[u][i]; dfs(y); dp[u][1] += dp[y][0]; if(f[y][0]==0)f[u][1] = 0; dp[u][0] += max(dp[y][0],dp[y][1]); if(dp[y][0]==dp[y][1])f[u][0] = 0; else if(dp[y][0]>dp[y][1]&&f[y][0]==0)f[u][0] = 0; else if(dp[y][0]<dp[y][1]&&f[y][1]==0)f[u][0] = 0; } } //獲取對應id int cnt; int id(const string& str) { if(!M.count(str)) M[str] = ++cnt; return M[str]; } int main() { while(cin>>n) { if(n==0)break; for(int i=1;i<=n;i++) v[i].clear(); //ans儲存人數,res儲存是否唯一 ans = 0;res = 1; M.clear(); cnt = 0; string a,b; cin>>a; id(a); //輸入的坑點就在這裡 for(int i=2;i<=n;i++) { cin>>a>>b; v[id(b)].push_back(id(a)); //M[a] = i;這是之前錯誤的寫法,因為我們不能保證M[b]一定存在。 //v[M[b]].push_back(M[a]); } dfs(1); ans = max(dp[1][0],dp[1][1]); if(dp[1][0]==dp[1][1]||(dp[1][0]>dp[1][1]&&f[1][0]==0)||(dp[1][0]<dp[1][1]&&f[1][1]==0)) res = 0; printf("%d %s\n",ans,res==0?"No":"Yes"); } return 0; }
UVA-1218-Perfect Service
題意:
有n個機器組成的樹形結構,要求一臺伺服器必須連線一臺電腦,求使用的最少的伺服器
分析
對於任意一個機器u,可以是伺服器也可以不是伺服器,由於有“不是伺服器的機器必須恰好與一個伺服器相鄰”的條件,又由於它是一棵樹,所以我們借用每個節點的父節點,來表示狀態。即分為三種情況
- u是伺服器。那麼與u管聯的任何機器可以是伺服器也可以不是
- u不是伺服器,u的父節點是伺服器。那麼u的所有子節點都不是伺服器
- u不是伺服器,u的父節點不是伺服器。那麼u必須恰好有一個子節點是伺服器
動態轉移:
d[u][0] = sum{ min(d[v][0],d[v][1])}
d[u][1] = sum{d[v][2]}
d[u][2] = min(d[u][1]-d[v][2]+d[v][0])
第三個含義是這樣的:因為要求v是伺服器的情況的最小值,並且u的其他子節點都不是伺服器。d[u][0]
儲存了u子節點都不是伺服器的最優解,d[v][2]
儲存了v不是伺服器的最優解,d[v][0]
儲存了v是伺服器的最優解。所以有上述式子可得d[u][2]
的最優解
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3fffffff
#define maxn 10010
vector<int> v[maxn];
int n,ans;
int d[maxn][3];
void dp(int x,int p)
{
d[x][0] = 1;
d[x][1] = 0;
d[x][2] = maxn;
int l = v[x].size();
for(int i=0;i<l;i++)
{
int y = v[x][i];
if(y==p)continue;
dp(y,x);
d[x][0] += min(d[y][0],d[y][1]);
d[x][1] += d[y][2];
}
//因為計算d[x][2]要用到d[x][1],而d[x][1]是累加計算,所以要分開兩次遍歷
for(int i=0;i<l;i++)
{
int y = v[x][i];
if(y==p)continue;
d[x][2] = min(d[x][2],d[x][1]-d[y][2]+d[y][0]);
}
}
int main()
{
while(~scanf("%d",&n)&&n!=-1)
{
if(n==0)continue;
for(int i=1;i<=n;i++)
v[i].clear();
int x,y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
dp(1,0);
//最後這裡的坑點,想一想為什麼沒有d[1][1]呢?
ans = min(d[1][0],d[1][2]);
cout<<ans<<endl;
}
return 0;
}