這場周賽是手速局hh
死或生
某國正在以投票的方式決定 2 名死刑犯(編號 1∼2)的生死。
共有 n 組人員(編號 1∼n)參與投票,每組 10 人。
每組成員只參與一名死刑犯的投票,其中第 i 組人員的投票物件是死刑犯 ti,其中 xi 人認為他無罪,yi 人認為他有罪。
在所有人員投票結束後,將對投票結果進行統計。
對於每名死刑犯,如果投他無罪的總票數大於或等於投他有罪的總票數,則他得以生還,否則他將被處死。
請你判斷每名死刑犯的生死。
輸入格式
第一行包含一個整數 n。
接下來 n 行,每行包含三個整數 ti,xi,yi。
保證兩名犯人都會被投票。
輸出格式
如果第一位死刑犯生還,則在第一行輸出 LIVE,否則在第一行輸出 DEAD。
如果第二位死刑犯生還,則在第二行輸出 LIVE,否則在第二行輸出 DEAD。
資料範圍
\(前 3 個測試點滿足 2≤n≤10。\)
\(所有測試點滿足 2≤n≤1000,1≤ti≤2,0≤xi,yi≤10,xi+yi=10。\)
輸入樣例1:
2
1 5 5
2 6 4
輸出樣例1:
LIVE
LIVE
輸入樣例2:
3
1 0 10
2 0 10
1 10 0
輸出樣例2:
LIVE
DEAD
簡單的列舉即可,藉助雜湊表記錄無罪票數和有罪票數
#include <bits/stdc++.h>
using namespace std;
int a[3],b[3];
int n;
int main()
{
cin>>n;
while (n--)
{
int t,x,y;
cin>>t>>x>>y;
a[t]+=x;
b[t]+=y;
}
for (int i=1;i<=2;i++)
if (a[i]>=b[i])
puts("LIVE");
else puts("DEAD");
return 0;
}
最大價值
已知,小寫字母 a∼z 的價值分別為$ w_a,w_b,…,w_z$。
對於一個由小寫字母構成的長度為 l 的字串 \(S=s_1,s_2…s_l,其價值為 w_{s1}×1+w_{s2}×2+…+w_{sl}×l\)。
現在,給定一個由小寫字母構成的字串 S,請你在這個字串中插入 k 個小寫字母,要求最終得到的字串的價值儘可能大。
注意:
- 插入的位置可以隨意選。
- 插入的字母也可以隨意選,可以插入不同字母。
輸出最大可能價值。
輸入格式
第一行包含一個字串 S。
第二行包含一個整數 k。
第三行包含 26 個整數 \(w_a,w_b,…,w_z\)。
輸出格式
一個整數,表示最大可能價值。
資料範圍
前 3 個測試點滿足,S 的長度範圍 [1,5]。
所有測試點滿足,S 的長度範圍 [1,1000],\(0≤k≤10^3\),\(w_a∼w_z\) 的取值範圍 [0,1000]。
輸入樣例:
abc
3
1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
輸出樣例:
41
貪心即可,經過證明(很好證)要得到最大價值,插入方法即在尾部插入k個單個價值的最大的字母
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
string s;
int w[N];
int k;
typedef long long LL;
int main()
{
cin>>s>>k;
int res=0;
for (int i=0;i<26;i++)
cin>>w[i],res=max(res,w[i]);
LL ans=0;
for (int i=0;i<s.size();i++)
ans+=(w[s[i]-'a']*(i+1)); // 這裡一定要記著-'a'啊!血的教訓,這細節要注意!
int len=s.size();
for (int i=len+1;i<=k+len;i++)
{
ans+=res*i;
}
cout<<ans<<endl;
return 0;
}
危險程度
有 n 種化學物質,編號 1∼n。
其中,有 m 對物質之間會發生反應。
現在,要將這些化學物質逐個倒入同一個試管之中,具體倒入順序不限。
我們需要計算一下試管的危險值。
已知,空試管的危險值為 1,每倒入一種化學物質,如果該物質能夠與之前倒入試管中的一種或多種物質發生反應,則試管的危險值將乘以 2。
請你計算並輸出,透過合理安排所有化學物質的倒入順序,能夠得到的試管的最大危險值。
輸入格式
第一行包含兩個整數 n,m。
接下來 m 行,每行包含兩個整數 x,y,表示化學物質 x 和化學物質 y 之間會發生反應。保證同一對化學物質在輸入中最多出現一次。
輸出格式
一個整數,表示最大危險值。
資料範圍
前 4 個測試點滿足 \(1≤n≤10。\)
所有測試點滿足 $ 1≤n≤50,0≤m≤n(n−1)2,1≤x<y≤n。$
輸入樣例1:
1 0
輸出樣例1:
1
輸入樣例2:
2 1
1 2
輸出樣例2:
2
輸入樣例3:
3 2
1 2
2 3
輸出樣例3:
4
題意中幾個很重要的性質抓出來
- 第一個放入的物品無法和其他物質反映,因為此時試管中沒有其他物品
- 不能相互反應的物品一定嚴格獨立,沒有交集
- 假設同一個反應體系中的物品數為k個,則該反應體系對危險程度的貢獻度為\(2^{k-1}\),因此我們可以看出,每一個反應體系實際就是一個連通塊,即每一個連通塊中的物品數量為\(k_i\),則該連通塊的作用即可為\(2^{k_i}-1\)
現在共有t個獨立的連通塊,則總的貢獻度為\(2^{k_1}-1\) * \(2^{k_2}-1\) * ... * \(2^{k_i}-1\) = \(2^{k_1+k_2+...+k_i-t}\)
我們注意到共有n件物品,因此結果為\(2^{n-t}\),題目瞬間轉化為求解獨立的連通塊的數量
1. 法一:並查集求解獨立連通塊數量
#include <bits/stdc++.h>
using namespace std;
const int N = 55;
int p[N];
int n,m;
typedef long long LL;
int find(int x)
{
if (p[x]!=x) p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for (int i=1;i<=n;i++) p[i]=i;
int cnt=n; // cnt為獨立集合的數量,即連通塊的數量
while (m--)
{
int a,b;
cin>>a>>b;
int pa = find(a);
int pb = find(b);
if (pa!=pb)
{
cnt--;
p[pa]=pb;
}
}
printf("%lld\n",1ll<<n-cnt);
return 0;
}
2. 建圖,圖的遍歷求解連通塊的數量
dfs寫法一
#include <bits/stdc++.h>
using namespace std;
const int N = 55;
bool st[N],reaction[N][N];
int n,m;
typedef long long LL;
LL ans=1;
void dfs(int x)
{
for (int i=1;i<=n;i++)
{
if (reaction[x][i]&&!st[i]) // 當前物品與1~i(除本身)有反應,即更新ans並順著這個物品遍歷
{
ans<<=1;
st[i]=true;
dfs(i);
}
}
}
int main()
{
cin>>n>>m;
while (m--)
{
int a,b;
cin>>a>>b;
reaction[a][b]=reaction[b][a]=true;
}
for (int i=1;i<=n;i++)
{
if (!st[i]) // 只要當前的物品還沒有用過
{
st[i]=true;
dfs(i);
}
}
cout<<ans<<endl;
return 0;
}
dfs寫法二
#include <bits/stdc++.h>
using namespace std;
const int N = 55 ,M = N*N; // 無向圖注意邊數
int e[M],h[N],ne[M],idx;
int n,m;
typedef long long LL;
bool st[N];
void add(int a,int b) // 經典鄰接表建圖add
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int x) // dfs遍歷無向圖
{
st[x]=true;
for (int i=h[x];~i;i=ne[i])
{
int j = e[i];
if (!st[j]) dfs(j);
}
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h); // 不初始化的後果就是TLE
while (m--)
{
int a,b;
cin>>a>>b;
add(a,b),add(b,a); // 無向圖即為特殊的有向圖
}
int cnt=0; // 求解連通塊的數量
for (int i=1;i<=n;i++)
{
if (!st[i])
{
dfs(i);
cnt++;
}
}
cout<<(1ll<<n-cnt)<<endl; // 答案為2^{n-cnt}
return 0;
}
bfs寫法
#include <bits/stdc++.h>
using namespace std;
const int N = 55 ,M = N*N; // 無向圖注意邊數
int e[M],h[N],ne[M],idx;
int n,m;
typedef long long LL;
bool st[N];
queue<int>q;
void add(int a,int b) // 經典鄰接表建圖add
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs(int x) // bfs遍歷無向圖
{
st[x]=true;
q.push(x); // 藉助stl
while (q.size())
{
auto t = q.front();
q.pop();
for (int i=h[t];~i;i=ne[i])
{
int j = e[i];
if (!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h); // 不初始化的後果就是TLE
while (m--)
{
int a,b;
cin>>a>>b;
add(a,b),add(b,a); // 無向圖即為特殊的有向圖
}
int cnt=0; // 求解連通塊的數量
for (int i=1;i<=n;i++)
{
if (!st[i])
{
bfs(i);
cnt++;
}
}
cout<<(1ll<<n-cnt)<<endl; // 答案為2^{n-cnt}
return 0;
}
總結
不論是並查集寫法,還是dfs和bfs寫法,本質都是求解圖中的連通子塊個數。
因此,我們對求解連通塊個數的題型,即可採用並查集和圖的遍歷這兩大類方法,其中圖的遍歷可以用dfs(程式碼簡潔)或者bfs(思路簡單,但藉助佇列實現程式碼量較為冗長)