A.數字遊戲
題意描述
小 K 同學向小 P 同學發送了一個長度爲 8 的 01 字符串來玩數字遊戲,小 P 同學想 要知道字符串中究竟有多少個 1。
注意:01 字符串爲每一個字符是 0 或者 1 的字符串,如“101”(不含雙引號)爲一 個長度爲 3 的 01 字符串。
解題思路
簽到題。
代碼示例
#include<bits/stdc++.h>
using namespace std;
string str;
int cnt = 0;
int main(){
cin >> str;
for(int i = 0;str[i];i++) if(str[i] == '1') cnt++;
cout << cnt << endl;
return 0;
}
B.公車換乘
題意描述
著名旅遊城市 B 市爲了鼓勵大家採用公共交通方式出行,推出了一種地鐵換乘公交 車的優惠方案:在搭乘一次地鐵後可以獲得一張優惠票,有效期爲 45 分鐘,在有效期內可以 消耗這張優惠票,免費搭乘一次票價不超過地鐵票價的公交車。在有效期內指 開始乘公交車的時間與開始乘地鐵的時間之差小於等於 45 分鐘,即:。
搭乘地鐵獲得的優惠票可以累積,即可以連續搭乘若干次地鐵後再連續使用優 惠票搭乘公交車。搭乘公交車時,如果可以使用優惠票一定會使用優惠票;如果有多張優惠票滿 足條件,則優先消耗獲得最早的優惠票。
現在你得到了小軒最近的公共交通出行記錄,你能幫他算算他的花費嗎?
解題思路
一道模擬題,顯而易見需要按照時間升序,因爲如果超過45分鐘就無效了。基於此應該想到可以利用優先隊列(或類似)的數據結構來維護,每次坐公交,在所有價值大於本次票價的優惠券中優先使用過期時間最早的(不用就過期了!),價值最低的優惠券。一旦票過期了,就刪掉,但是如果票只是價值不夠,那可能後面還會有用,所以還要壓回去。
代碼示例
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
struct Node{
int ty;
ll t,price;
bool operator < (const Node& rhs) const{
if(t == rhs.t) return price > rhs.price;
return t > rhs.t;
}
}rd[N];
int n;
priority_queue<Node> q;
stack<Node> s;
void solve(){
sort(rd+1,rd+1+n);
ll res = 0;
for(int i = n;i >= 1;i--){
if(!rd[i].ty){
res += rd[i].price, q.push(rd[i]);
continue;
}
bool flag = true;
while(q.size()){
Node x = q.top(); q.pop();
if(rd[i].t - x.t > 45) continue;
if(x.price < rd[i].price){
s.push(x); continue;
}
flag = false; break;
}
if(flag) res += rd[i].price;
while(s.size()) q.push(s.top()), s.pop();
}
printf("%lld\n",res);
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++)
scanf("%d%d%d",&rd[i].ty,&rd[i].price,&rd[i].t);
solve();
return 0;
}
C.紀念品
題意描述
小偉突然獲得一種超能力,他知道未來 T 天 N 種紀念品每天的價格。某個紀念品 的價格是指購買一個該紀念品所需的金幣數量,以及賣出一個該紀念品換回的金幣數量。
每天,小偉可以進行以下兩種交易無限次:
任選一個紀念品,若手上有足夠金幣,以當日價格購買該紀念品;賣出持有的任意一個紀念品,以當日價格換回金幣。
每天賣出紀念品換回的金幣可以立即用於購買紀念品,當日購買的紀念品也可以當日賣出換回金幣。當然,一直持有紀念品也是可以的。
T 天之後,小偉的超能力消失。因此他一定會在第 T 天賣出所有紀念品換回金幣。
小偉現在有 M 枚金幣,他想要在超能力消失後擁有儘可能多的金幣。
解題思路
本題思路可以根據數據範圍來想。這是一道動態規劃題。
剛開始想到的是建立一張全圖,但是後來發現節點數量不是 N 而是 NT ,並且總價值也不一定只能走一條路。
後來發現可以用動態規劃解決,首先設 f[x]: 第x天能夠賺到最多多少錢。那麼顯然f[1] = m,並且 f 是遞增的。重點在於狀態轉移,f[ i ] 肯定是由 f[ 1~i-1 ] 推出來的,因爲單獨一個 f[i-1] 無法表示所有狀態。
假設當前處於狀態 j (j < i),那麼將第 j 天物品的價格看作花費,第 i 天的物品價格看作價值,這就是一個完全揹包問題,揹包容量是當天的金幣數 f[j] 。
於是我們兩層循環O(T^2)來計算 f[i] ,而每次計算需要O(MN) 做完全揹包,但是實際測試效率還行,可能是數據太弱了。
代碼示例
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2+10;
const int M = 1e4+10;
int t,n,m;
int val[N][N];
int f[N]; //第 x 天最多有多少錢
int tmp[M],mx;
void solve(){
f[1] = m;
for(int day = 2;day <= t;day++){
for(int i = 1;i < day;i++){
memset(tmp,0,sizeof tmp); mx = m;
for(int j = 1;j <= n;j++){
for(int v = val[i][j]; v <= f[i];v++){
tmp[v] = max(tmp[v-val[i][j]]+val[day][j],tmp[v]);
mx = max(mx,tmp[v]+f[i]-v);
}
}
f[day] = max(f[day],mx);
}
f[day] = max(f[day],f[day-1]);
}
//for(int i = 1;i <= t;i++) printf("%d\n",f[i]);
printf("%d\n",f[t]);
}
int main(){
scanf("%d%d%d",&t,&n,&m);
for(int i = 1;i <= t;i++)
for(int j = 1;j <= n;j++) scanf("%d",&val[i][j]);
solve();
return 0;
}
D.加工零件
題意描述
凱凱的工廠正在有條不紊地生產一種神奇的零件,神奇的零件的生產過程自然也很 神奇。工廠裏有 𝑛 位工人,工人們從 1∼𝑛編號。某些工人之間存在雙向的零件傳送帶。保證每兩名工人之間最多隻存在一條傳送帶。
如果 𝑥 號工人想生產一個被加工到第 𝐿(𝐿>1) 階段的零件,則所有與 𝑥 號工人 有傳送帶直接相連的工人,都需要生產一個被加工到第 𝐿 −1 階段的零件(但 𝑥 號工 人自己無需生產第 𝐿 −1 階段的零件)。
如果 𝑥 號工人想生產一個被加工到第 1 階段的零件,則所有與 𝑥 號工人有傳送 帶直接相連的工人,都需要爲 𝑥 號工人提供一個原材料。
軒軒是 1 號工人。現在給出 𝑞 張工單,第 𝑖 張工單表示編號爲 𝑎𝑖的工人想生產 一個第 𝐿𝑖階段的零件。軒軒想知道對於每張工單,他是否需要給別人提供原材料。他知道聰明的你一定可以幫他計算出來!
解題思路
經過推導可以發現,如果 a 號節點到 1 號節點(以下只用編號簡寫)之間存在一條小於 L 的路徑,且它們的差爲偶數(包括0),那麼 1 就要提供原材料。
但是問題在於 1 到 a 之間可能不止一條路徑,而 L 也不一定是奇或者偶,於是我們需要保存 a 到 1 的最短奇路徑與最短偶路徑。
在圖論中,如果邊權爲1,那麼求奇偶最短路徑的好方法就是用分層圖來做,具體做法是建立雙層圖,如果從起點不在同一層,則路徑一定是奇,否則一定爲偶;這樣做的好處是不用額外的輔助代碼,只要在建圖時多加兩條邊就行了。
代碼示例
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
const int M = 2*N;
typedef long long ll;
int n,m,q;
int head[N],edge[M],ver[M],nex[M] ,tot = 1;
void addEdge(int x,int y,int z){
ver[++tot] = y; edge[tot] = z;
nex[tot] = head[x]; head[x] = tot;
}
int getInt(){
int res = 0;
bool neg = false;
char c = getchar();
while(c != '-' && (c < '0' || c > '9')) c = getchar();
if(c == '-') neg = true, c = getchar();
while(c >= '0' && c <= '9') res = res*10+c-'0',c = getchar();
return neg?-res:res;
}
queue<int> que;
int dis[N],vis[N];
bool solve(int a,int l){
if(dis[a] <= l && !((dis[a]&1) ^ (l&1))) return true;
if(dis[a+n] <= l && !((dis[a+n]&1) ^ (l&1))) return true;
return false;
}
void SPFA(int s){
que.push(s);
memset(dis,0x3f,sizeof dis);
dis[s] = 0;
while(que.size()){
int x = que.front(); que.pop();
vis[x] = false;
for(int i = head[x];i ;i = nex[i]){
int y = ver[i], z = edge[i];
if(dis[y] > dis[x]+z){
dis[y] = dis[x]+z;
if(!vis[y]) que.push(y),vis[y] = true;
}
}
}
}
int main(){
n = getInt(); m = getInt(); q = getInt();
for(int i = 1,x,y;i <= m;i++){
x = getInt(); y = getInt();
addEdge(x,y+n,1); addEdge(y+n,x,1);
addEdge(x+n,y,1); addEdge(y,x+n,1);
}
SPFA(1);
//for(int i = 1;i <= n<<1;i++) cout << dis[i] << endl;
for(int i = 1,a,l;i <= q;i++){
a = getInt(); l = getInt();
if(solve(a,l)) puts("Yes");
else puts("No");
}
return 0;
}