P1268 樹的重量 (思惟題)node
題意:ios
首先定義樹的重量爲數上每一個邊權的和,如今給你每一個葉子結點之間的最短距離,讓你求出樹的重量數組
思路:ide
先定義葉子節點之間的距離爲dis[u,v]spa
那麼咱們會先考慮只有兩個節點的狀況,那麼答案就爲dis[u,v]code
當n=3,咱們考慮將3號節點加入樹中,因爲每一個節點都是葉子節點,那麼3號節點只能由一條邊鏈接至1與2的連線上blog
長度len = (dis(1,3)+dis(2,3)-dis(1,2))/2排序
n>3的狀況也同理。枚舉i,看看點n是否是從點1~i的路徑上分叉出來的,求出的最小len就是要加到答案裏面去的隊列
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #define inf 0x3f3f3f3f using namespace std; const int maxn=35; int dis[maxn][maxn]; int main() { int n,x; while(scanf("%d",&n)&&n){ memset(dis,0,sizeof(dis)); for(int i=1;i<n;++i){ for(int j=i+1;j<=n;++j){ scanf("%d",&x); dis[i][j]=x,dis[j][i]=x; } } int ans=dis[1][2]; for(int i=3;i<=n;i++){ int len=inf; for(int j=1;j<i;j++){ for(int k=1;k<j;k++) len=min(len,(dis[i][j]+dis[k][i]-dis[k][j])>>1); } ans+=len; } cout<<ans<<endl; } }
P1113 雜務(拓撲排序)string
題意:
有n個任務,每一個任務完成都有其所需的時間,而且有其前置任務,問完成全部任務要多久(沒有直接關係的任務能夠同時開始)
思路:
本身寫的稍稍有些複雜
首先建圖,對於一個任務給全部其前置任務連一條有向邊(從前置連向本身),並記錄一個點的入讀
以前遍歷一遍,將入度爲0的點加入隊列中,邊將其全部相鄰的點入度減一,若是有入度爲0的點,則加入隊列中
比較關鍵的一點就是如何更新時間dp[nex]=max(dp[nex],dp[i]+tim[nex])
個人寫法較爲複雜,還多用了一個隊列存時間,用一個DP數組就能直接解決了
#include<iostream> #include<algorithm> #include<queue> #include<vector> #include<cstdio> #include<cstring> #define inf 0x3f3f3f3f using namespace std; const int maxn=1e5+10; queue<int> a,b; vector<int> edge[maxn]; int in[maxn],tim[maxn],ans,n,mx[maxn]; void solve() { for(int i=1;i<=n;i++){ if(!in[i]){ a.push(i),b.push(tim[i]); } } while(!a.empty()){ int x=a.front(),y=b.front(); a.pop(),b.pop(); ans=max(ans,y); for(int i=0;i<edge[x].size();i++){ in[edge[x][i]]--; mx[edge[x][i]]=max(y,mx[edge[x][i]]); if(!in[edge[x][i]]){ a.push(edge[x][i]); b.push(tim[edge[x][i]]+mx[edge[x][i]]); } } } } int main() { int x,y,temp; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&x,&y); tim[x]=y; while(scanf("%d",&temp)&&temp){ edge[temp].push_back(x); in[x]++; } } memset(mx,-inf,sizeof(mx)); ans=-inf; solve(); cout<<ans<<endl; }
P1525 關押罪犯(二分圖/並查集)
題意:
給你m對矛盾關係,每對關係分別涉及到x,y兩人,矛盾值爲w
請你判斷分配x和y到兩個集合中,可否避免衝突
如能避免請輸出0,若是衝突不可避免,請輸出最小的矛盾值
思路:
方法①:並查集
並查集能維護連通性、傳遞性,通俗地說,親戚的親戚是親戚
。
咱們不妨這樣想:兩我的a,b有仇,那麼把他們放在一塊兒顯然會打起來,那麼咱們還不如把a與b的其餘敵人放在一塊兒,
由於這樣可能會出現「敵人的敵人就是朋友」的狀況,剛好a與b的其餘敵人之間沒有矛盾,那麼他們就能夠放在同一個集合中,反之b對a亦然
咱們能夠將沒對關係按照權值從大到小排序,這樣能夠保證一旦發生衝突,答案是最小的
對於加入的每段關係,咱們先判斷他們是否在同一集合內,若是在的話就說明發生衝突,直接輸出答案
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; const int maxn1=2e4+10; const int maxn2=1e5+10; int fa[maxn1],ene[maxn1]; struct node{ int u,v,w; }edge[maxn2]; int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));} int cmp(node a,node b){return a.w>b.w;} int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); for(int i=1;i<=n;i++) fa[i]=i; memset(ene,0,sizeof(ene)); sort(edge+1,edge+1+m,cmp); for(int i=1;i<=m;i++){ int u=edge[i].u,v=edge[i].v,w=edge[i].w; int f1=find(u),f2=find(v); if(f1==f2){ cout<<w<<endl; return 0; } else{ if(!ene[u]) ene[u]=v; else fa[find(ene[u])]=find(v); if(!ene[v]) ene[v]=u; else fa[find(ene[v])]=find(u); } } cout<<0<<endl; return 0; }
方法②:二分圖
看到將犯人分紅兩批應該很容易想到二分圖的作法
那麼很明顯,不是全部全部狀況下都能講犯人分紅兩部分,一定有一些衝突是沒法避免的
咱們也能夠注意到,衝突是具備單調性的,咱們就能夠想到二分的作法
咱們二分答案,設當前二分的值爲mid,此時任意兩個矛盾雙方x和y必須被分在兩個不一樣集合中,將罪犯們做爲節點,在矛盾值大於等於mid的罪犯之間連一條邊,咱們獲得一張無向圖。此時咱們只需斷定這張無向圖是否爲二分圖便可(由於要分爲兩部分),若是是二分圖,令二分右端點R=mid,不然令L=mid便可
#include<iostream> #include<algorithm> #include<cstring> #include<queue> #include<cstdio> using namespace std; const int maxn=2e5+10; struct edge{ int u,v,w; }p[maxn]; int n,m,L=0,R=0,cnt=0,head[maxn]; void add_edge(int x,int y,int w) { p[++cnt].u=head[x]; head[x]=cnt; p[cnt].v=y; p[cnt].w=w; } bool judge(int mid) { queue<int> q; int color[maxn]={0}; for(int i=1;i<=n;i++){ if(!color[i]) color[i]=1,q.push(i); while(!q.empty()){ int x=q.front(); q.pop(); for(int j=head[x];j;j=p[j].u){ if(p[j].w>=mid){ if(!color[p[j].v]){ q.push(p[j].v); if(color[x]==1) color[p[j].v]=2; else color[p[j].v]=1; } else if(color[p[j].v]==color[x]) return false; } } } } return true; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); R=max(R,w); add_edge(u,v,w); add_edge(v,u,w); } int ans=0; R++; while(L+1<R){ int mid=(L+R)>>1; if(judge(mid)) R=mid; else L=mid; } cout<<L<<endl; return 0; }