主要是自己打給自己看的,如果有錯還請下面留言糾正我==
如果是初階高中競賽程式選手或者目標檢定可以稍微參考
這邊主要是大量程式碼示範+提供關鍵字
Yuihuang的演算法筆記
Zerojudge
台大演算法筆記
APCSCAMP(營隊)
IOICAMP(營隊)(很進階,主攻數學的我不會去)
有我的AC CODE喔
預計下一場次(不確定ww只能說至少要六月,一月的跟清大數陪衝撞)
1.2022 10月 APCS(成績:觀念4級分/實作4級分)
#define int long long
#define wh_ale ios::sync_with_stdio(0)
特殊的(抱歉啦其他的我有空再慢慢補)
s.find(element)
代表搜尋element
是否在set裡面
s.insert(element)
代表插入element
到set裡面
s.erase(element)
代表刪除set裡面的元素element
*s.begin()
代表找出set裡面最小值
*s.rbegin()
代表找出set裡面最大值
CODE:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
set<int> s;
int i, n, cmd, x;
cin >> n;
for(i=0;i<n;i++){
cin >> cmd;
if(cmd==1){
cin >> x;
if((s.find(x) != s.end())==false){
s.insert(x);
}
}
else if(cmd==2){
cin >> x;
if((s.find(x) != s.end())==false){
cout << "error\n";
}
else s.erase(x);
}
else if(cmd==3){
cout << s.size()<<'\n';
}
else if(cmd==4){
if(s.size() != 0){
cout << *s.begin()<<'\n';
}
else cout <<"error\n";
}
else if(cmd==5){
if(s.size() != 0){
cout << *s.rbegin()<<'\n';
}
else cout <<"error\n";
}
}
return 0;
}
敘述:輸入n代表有幾個數字,接著輸入n個數字求所有排列(暴力破解)
關鍵:while(next_permutation(v.begin(), v.end()))
函數的使用方式
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define pkb push_back
#define ppb pop_back
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n, x, i;
vector<int> v;
cin >> n;
for(i=0;i<n;i++){
cin >> x;
v.pkb(x);
}
sort(v.begin(), v.end());
for(i=0;i<n;i++){
cout <<v[i] <<" ";
}
cout <<'\n';
while(next_permutation(v.begin(), v.end())){
for(i=0;i<n;i++){
cout <<v[i] <<" ";
}
cout <<'\n';
}
return 0;
}
敘述:給定n筆測資,每次m個數字、要取k個做字典序排列並輸出
關鍵:排序後利用遞迴的概念(放入和取出很抽象)
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
int n, k;
vector<int> v, a;
void run(int i){
if(i==n){
if(v.size()==k){
for(int j=0;j<k;j++){
cout << v[j]<<" ";
}
cout << '\n';
}
}
else{
v.push_back(a[i]);
run(i+1);
v.pop_back();
run(i+1);
}
}
int main() {
// your code goes here
int t;
cin >> t;
for(int i=0;i<t;i++){
cin >> n >> k;
a.resize(n);
for(int j=0;j<n;j++){
cin >> a[j];
}
sort(a.begin(),a.end());
run(0);
}
return 0;
}
敘述:點我
關鍵:遞迴枚舉
AC CODE:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, p, ans=0;
int t[50];
void run(int x, int m){
if(x==n){
if(m<p){
ans=max(ans, m);
}
}else{
run(x+1, m+t[x]);
run(x+1, m);
}
}
signed main() {
cin >> n >> p;
for(int i=0;i<n;i++){
cin >> t[i];
}
run(0, 0);
cout << ans << '\n';
return 0;
}
敘述:給定n個錢幣種類,每種無限且互相整除,求兌換k元的最少硬幣數
關鍵:取最大值開始嘗試,一路到最小(取mod)
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
ll i, n, k, c[10], cnt;
cin >> n >> k;
for(i=0;i<n;i++){
cin >> c[i];
}
for(i=n-1;i>=0;i--){
cnt += (k-k%c[i])/c[i];
k = k%c[i];
}
cout << cnt <<'\n';
return 0;
}
變形
敘述:呈上,但零錢數量有限(一開始會给定數量)
關鍵:取最大值開始嘗試,直到無法再取,一路到最小(取mod)
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define wh_ale ios::sync_with_stdio(0);cin.tie(0)
int main() {
wh_ale;
ll i, j, n, k, c[10], a[10], cnt=0;
cin >> n >> k;
for(i=0;i<n;i++){
cin >> c[i];
}
for(i=0;i<n;i++){
cin >> a[i];
}
for(i=n-1;i>=1;i--){
if(k>=c[i]){
if(k/c[i]<a[i]){
cnt += k/c[i];
k = k%c[i];
}
else{
k -= c[i]*a[i];
cnt += a[i];
}
}
}
if(k != 0){
cnt += k;
}
cout << cnt <<'\n';
return 0;
}
敘述:給定一數列,求最大平均區間
關鍵:利用數學得知最大平均區間要碼2要碼3
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n, a[300001], i, j, mod;
int mx=0;
cin >> n;
for(i=0;i<n;i++){
cin >> a[i];
}
for(i=0;i<n-1;i++){
if(max(mx, (a[i]+a[i+1])*3) != mx) mod=2;
mx=max(mx, (a[i]+a[i+1])*3);
}
for(i=0;i<n-2;i++){
if(max(mx, (a[i]+a[i+1])*2) != mx) mod=3;
mx=max(mx, (a[i]+a[i+1])*2);
}
if(mod==2){
if(mx%2==0) cout << mx/6 <<" "<<1 <<'\n';
else cout << mx/3 << " " <<2<<'\n';
}
else{
if(mx%3==0) cout << mx/6 <<" "<<1 <<'\n';
else cout << mx/3 << " " <<3<<'\n';
}
return 0;
}
敘述:給定n個物品價值與重量(重量互相整除)以及一個背包的最大負重
關鍵:利用平均最大重量排序,一一放入背包
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int, int>
bool cmp(pii a, pii b){
if(a.first/a.second != b.first/b.second) return a.first/a.second>b.first/b.second;
else return a.second<b.second;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
ll n, m, i, j, cct=0;
int cnt=0;
pii c[200000];
cin >> n >> m;
for(i=0;i<n;i++){
cin >> c[i].first >> c[i].second;
}
sort(c, c+n, cmp);
for(i=0;i<n;i++){
if(cnt+c[i].second<=m){
cct += c[i].first*(((m-cnt)-((m-cnt)%c[i].second))/c[i].second);
cnt += ((m-cnt)-((m-cnt)%c[i].second));
}
}
cout <<cct <<'\n';
return 0;
}
敘述:給定一堆工作開始與結束時間,求某區段內能完成工作最大量
關鍵:利用結束時間做相關排序(結束時間越早,且越晚開始越有利)
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
struct node{
int f;
int s;
};
bool cmp(node a, node b){
return a.s<b.s;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n, i, j, cnt=0, l=0;
node t[500000];
cin >> n;
for(i=0;i<n;i++){
cin >> t[i].f >> t[i].s;
}
sort(t, t+n, cmp);
for(i=0;i<n;i++){
if(l>t[i].f) cnt++;
else l=t[i].s;
}
cout <<n-cnt<<'\n';
return 0;
}
敘述:給定多個字串,求出最小結合字典序排列
關鍵:利用C++方便的字典序直接比較功能return a+b<b+a;
當成cmp函數做排序
範例程式碼:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
bool cmp(string a, string b){
return a+b<b+a;
}
int main(){
string s[100001];
int n;
cin >> n;
for(int i=0;i<n;i++){
cin >> s[i];
}
sort(s, s+n, cmp);
for(int j=0;j<n;j++){
cout << s[j];
}
}
敘述:給定物品重量與數量做堆疊,求最少移動次數
關鍵:利用特殊條件做交換性排序
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);cin.tie(0)
#define ll long long
//William
struct turtle{
int w;
int f;
};
bool cmp(turtle a, turtle b){
//麻煩到別人越高放越下面
return a.w*b.f>a.f*b.w;
}
int main() {
wh_ale;
turtle t[100000];
ll n, i, j, sum=0, ans=0;
cin >> n;
for(i=0;i<n;i++){
cin >> t[i].w;
sum += t[i].w;
}
for(i=0;i<n;i++){
cin >> t[i].f;
}
sort(t, t+n, cmp);
for(i=0;i<n;i++){
ans += (sum-t[i].w)*t[i].f;
sum -= t[i].w;
}
cout << ans <<'\n';
return 0;
}
敘述:廣度優先搜尋遍歷一張圖
關鍵:小細節(是否重複搜尋原點,要先確認有沒有越界在更新陣列)
範例程式碼(方格式BFS問題):
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int, int>
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n, m, s, t, i, j;
pii pr;
int cs[500][500];
int dx[4]={1,0,-1,0};
int dy[4]={0,-1,0,1};
queue<pii> q;
char c;
cin >> n >> m;
cin >> s >> t;
s--;
t--;
for(i=0;i<n;i++){
for(int j=0;j<m;j++){
cin >> c;
if(c=='X'){
cs[i][j]=-1;
}
else if(i==s and j==t){
cs[i][j]=0;
}
else cs[i][j] =-2;
}
}
q.push({s, t});
while(!q.empty()){
pr=q.front();
q.pop();
for(i=0;i<4;i++){
if(0<=pr.first+dx[i] and pr.first+dx[i]<n and 0<=pr.second+dy[i] and pr.second+dy[i]<m){
if(cs[pr.first+dx[i]][pr.second+dy[i]]==-2){
cs[pr.first+dx[i]][pr.second+dy[i]]=cs[pr.first][pr.second]+1;
q.push({pr.first+dx[i],pr.second+dy[i]});
}
}
}
}
for(i=0;i<n;i++){
for(int j=0;j<m-1;j++){
if(cs[i][j] != -2){
cout << cs[i][j]<<" ";
}
else cout << -1 << " ";
}
if(cs[i][m-1]!=-2) cout << cs[i][m-1]<<'\n';
else cout << -1 <<'\n';
}
return 0;
}
(線段式BFS十分雷同類似,但要注意是否重物搜尋原點的條件)
(APCS某年歷屆AC CODE)
#include <bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);
//William
int main() {
wh_ale;
int n, s[1000000], i, j, dx[2], p, cnt[1000000]={}, now;
queue<int> q;
bool y=false;
cin >> n >> p >> dx[0] >> dx[1];
dx[0]=0-dx[0];
for(i=0;i<n;i++){
cin >> s[i];
}
q.push(0);
while(!q.empty()){
now=q.front();
q.pop();
for(i=0;i<2;i++){
if(s[now+dx[i]]!=0 and 0<s[now+dx[i]] and s[now+dx[i]]<n and cnt[s[now+dx[i]]]==0){
q.push(s[now+dx[i]]);
cnt[s[now+dx[i]]]=cnt[now]+1;
}
}
}
if(cnt[p]==0) cout << -1 <<'\n';
else cout <<cnt[p]<<'\n';
return 0;
}
敘述:深度優先搜尋遍歷一張圖
關鍵:小細節(把賦值也寫成dfs函數的變數)
範例程式碼(本題是求圖有幾塊):
#include <bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define pb push_back
vector<int> v[100001];
int cg[100001]={};
int n, cnt=-1;
void dfs(int x){
cg[x]=1;
for(int i=0;i<v[x].size();i++){
if(cg[v[x][i]]==0){
dfs(v[x][i]);
}
}
}
int main() {
wh_ale;
int m, i, j, a, b;
cin >> n >> m;
for(i=0;i<m;i++){
cin >> a >> b;
v[a].pb(b);
v[b].pb(a);
}
for(i=1;i<=n;i++){
if(cg[i] == 0){
cnt++;
dfs(i);
}
}
cout << cnt <<'\n';
return 0;
}
敘述:用各種輸入操作DJS的合併,查詢,大小,組合數
關鍵:小細節(要把指向寫清楚,大合併小(除非題目有特例))
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int g[1000001], k[1000001], ib[1000001]={};
int n, m;
int fp(int x){
if(g[x]==x) return x;
else return fp(g[x]);
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int i, j, x, y;
cin >> n >> m;
for(i=1;i<=n;i++){
g[i]=i;
k[i]=1;
ib[i]=1;
}
int cmd, cnt=0;
while(m--){
cin >> cmd;
if(cmd==1){
cin >> x >> y;
x=fp(x);
y=fp(y);
cnt += k[x]*k[y];
if(k[x]>k[y]){
g[y]=g[x];
k[x]+=k[y];
k[y]=k[x];
}
else{
ib[x]=1;
g[x]=g[y];
k[x]+=k[y];
k[y]=k[x];
}
}
else if(cmd==2){
cin >> x >> y;
if(fp(x)==fp(y)) cout << "Yes\n";
else cout << "No\n";
}
else if(cmd==3){
cin >> x;
cout << k[fp(x)] <<'\n';
}
else{
cout << cnt <<'\n';
}
}
return 0;
}
敘述:每次拔掉一個節點,以此為順序直到結束對圖的動作
關鍵:記得統計大小,方向不要搞混就好
範例程式碼(本題是求圖有幾塊):
#include <bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define int long long
#define pb push_back
signed main (){
wh_ale;
int n, m, i, j;
vector<int> v[100001];
cin >> n >> m;
int x, y;
int d[100001]={};
for(i=0;i<m;i++){
cin >> x >> y;
v[x].pb(y);
d[y]++;
}
queue<int> q, qq;
for(i=1;i<=n;i++){
if(d[i]==0){
q.push(i);
}
}
while(!q.empty()){
x=q.front();
q.pop();
qq.push(x);
for(i=0;i<v[x].size();i++){
d[v[x][i]]--;
if(d[v[x][i]]==0) q.push(v[x][i]);
}
}
if(qq.size()!=n) cout << "IMPOSSIBLE\n";
else{
cout << "POSSIBLE\n";
while(qq.size()!= 1){
cout << qq.front() <<" ";
qq.pop();
}
cout << qq.front();
}
return 0;
}
敘述:求最小生成樹
關鍵:排序邊長,利用併查集檢查環
範例程式碼:
#include<bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define int unsigned long long
struct e{
int x, y, cost;
};
bool cmp(e x, e y){
return x.cost < y.cost;
}
int p[2000001], n, m, j, k, c, ans;
vector <e> v;
int find(int a){
if (p[a] == a) return a;
else {
p[a] = find(p[a]);
return p[a];
}
}
signed main() {
wh_ale;
cin >> n >> m;
if (n == 0 && m == 0) ans = 0;
v.clear();
for (int i = 1; i <= n; i++){
p[i] = i;
}
for (int i = 0; i < m; i++){
cin >> j >> k >> c;
v.push_back({j, k, c});
}
sort(v.begin(), v.end(), cmp);
for (e tmp:v){
j = find(tmp.x);
k = find(tmp.y);
if (j == k) continue;
p[k] = j;
ans += tmp.cost;
n--;
}
cout << ans << "\n";
}
敘述:求有向圖最短路徑
關鍵:每次都用dp更新距離
範例程式碼:
#include<bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define int long long
#define INF 1e18
#define pii pair<int, int>
#define psb push_back
vector<pii> g[100009];
int dis[100009];
int n, m, s, t;
pii r;
int pt;
int vis[100009]={};
priority_queue<pii, vector<pii>, greater<pii>> pq;
queue<int> q;
signed main(){
wh_ale;
cin >> n >> m;
int i;
for(i=1;i<=n;i++){
dis[i]=INF;
}
int a, b, c;
while(m--){
cin >> a >> b >> c;
g[a].psb({c, b});
}
cin >> s >> t;
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty()){
for(i=0;i<g[q.front()].size();i++){
if(dis[g[q.front()][i].second]>dis[q.front()]+g[q.front()][i].first){
dis[g[q.front()][i].second]=dis[q.front()]+g[q.front()][i].first;
pq.push({dis[g[q.front()][i].second], g[q.front()][i].second});
}
}
q.pop();
r=pq.top();
while(vis[r.second]!=0 and pq.size() != 0){
pq.pop();
r=pq.top();
}
if(vis[r.second]==0)q.push(r.second);
vis[r.second]=1;
pq.pop();
}
if(dis[t]<INF) cout << dis[t] <<'\n';
else cout << -1 <<'\n';
return 0;
}
敘述:樓梯問題國小競賽數學
關鍵:費氏數列
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define MOD 1000000007
int main() {
wh_ale;
int n, i, j;
ll dp[5000001]={};
dp[0]=1;
dp[1]=1;
cin >> n ;
for(i=2;i<=n;i++){
dp[i]=(dp[i-1]+dp[i-2])%MOD;
}
cout << dp[n] <<'\n';
return 0;
}
敘述:平面版樓梯問題
關鍵:駐馬法
範例程式碼
#include <bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 1000000000000000000
#define MOD 1000000007
int main() {
wh_ale;
int n, m, i, j, q, a, b;
bool t=true;
cin >> n >> m >> q;
ll g[1000][1000], dp[1000][1000];
for(i=0;i<n;i++){
for(j=0;j<m;j++){
cin >> g[i][j];
}
}
for(i=0;i<n;i++){
if(g[i][0]==0) dp[i][0]=1;
else{
break;
t=false;
}
}
if(t==false){
for(j=i;j<n;j++){
dp[j][0]=0;
}
}
t=true;
for(i=0;i<m;i++){
if(g[0][i]==0) dp[0][i]=1;
else{
break;
t=false;
}
}
if(t==false){
for(j=i;j<m;j++){
dp[0][j]=0;
}
}
for(i=1;i<n;i++){
for(j=1;j<m;j++){
if(g[i][j]==0){
dp[i][j]=(dp[i-1][j]+dp[i][j-1])%MOD;
}
else dp[i][j]=0;
}
}
for(i=0;i<q;i++){
cin >> a >> b;
cout << dp[a][b] <<'\n';
}
return 0;
}
敘述:給定平面迷宮,從左上到右下(每格都有點數),取最大點數總和
關鍵:利用轉移式dp[i][j]=max(dp[i-1][j], dp[i][j-1])+g[i][j]
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 1000000000000000000
int main() {
wh_ale;
int n, m, i, j, cx, cy;
cin >> n >> m;
vector<ll> g[100001], dp[100001];
for(i=0;i<=n;i++){
g[i].resize(m+1);
dp[i].resize(m+1);
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cin >> g[i][j];
}
}
for(i=0;i<=m;i++){
dp[0][i]=0;
}
for(i=0;i<=n;i++){
dp[i][0]=0;
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
dp[i][j]=max(dp[i-1][j], dp[i][j-1])+g[i][j];
}
}
cout << dp[n][m] <<'\n';
return 0;
}
敘述:給定數列,每次同色不能相鄰,三種顏色分別是不同的乘法運算
關鍵:三階段轉換並且最後比大小
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define ll long long
int main() {
ll n, dp[1000001][3], i, j, a[1000001][3], mx;
cin >> n;
dp[0][0]=0;
dp[0][1]=0;
dp[0][2]=0;
for(i=1;i<=n;i++){
for(j=0;j<=2;j++){
cin >> a[i][j];
}
}
for(i=1;i<=n;i++){
for(j=0;j<=2;j++){
dp[i][j]=a[i][j]+max(dp[i-1][(j+2)%3], dp[i-1][(j+1)%3]);
}
}
mx=max({dp[n][0], dp[n][1], dp[n][2]});
cout << mx <<'\n';
return 0;
}
敘述:要馬拿要馬不拿的背包問題
關鍵:滾動式,由大到小跑
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int main() {
wh_ale;
int n, i, j, w;
ll dp[100001]={};
int a[100], v[100];
cin >> n >> w;
for(i=0;i<n;i++){
cin >> a[i] >> v[i];
}
for(j=0;j<n;j++){
for(i=w;i>=a[j];i--){
dp[i]=max(dp[i-a[j]]+v[j], dp[i]);
}
}
cout << dp[w] <<'\n';
return 0;
}
敘述:給兩字串求編輯距離
關鍵:轉移式有點複雜,背下來(看程式碼就懂了)
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int main() {
string s, t;
int l, r, k;
int n, i, j, dp[2001][2001];
cin >> s >> t;
l=s.size();
r=t.size();
dp[0][0]=0;
for(i=1;i<=l;i++){
dp[i][0]=dp[i-1][0]+1;
}
for(i=1;i<=r;i++){
dp[0][i]=dp[0][i-1]+1;
}
for(i=1;i<=l;i++){
for(j=1;j<=r;j++){
if(s[i-1]==t[j-1]){
dp[i][j]=min({dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]});
}
else dp[i][j]=min({dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+1});
}
}
cout << dp[l][r];
return 0;
}
敘述:最大遞增子序列
關鍵:從後往前DP
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int main() {
wh_ale;
int n, i, j;
ll dp[5000]={};
ll a[5000], mx=1, b[5000];
cin >> n ;
dp[n-1]=1;
for(i=0;i<n;i++){
cin >> a[i];
}
for(i=n-2;i>=0;i--){
dp[i]=1;
b[i]=1;
for(j=i+1;j<n;j++){
if(a[i]<a[j]){
dp[i]=max(dp[i], b[i]+dp[j]);
}
}
mx=max(dp[i], mx);
}
cout << mx <<'\n';
return 0;
}
敘述:給定數列,每次合併都要花費兩堆總和,求最小合併花費總和
關鍵:轉移式很奇怪,dp[i][j]記錄左界右界合併最小值
範例程式碼:
#include <bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define ll long long
#define int long long
#define INF 99999999999999999
signed main() {
wh_ale;
int n, i, j;
ll a[501], cc[501]={}, dp[501][501]={};
cin >> n;
for(i=1; i<=n; i++) {
cin >> a[i];
cc[i] = cc[i-1] + a[i];
}
for(i=1; i<=n; i++){
for(j = 1; j <= n; j++){
dp[i][j] = INF;
}
}
for(i=1;i<=n;i++){
dp[i][i]=0;
}
for(i=1; i<n; i++){
for(j=1; j<=n-i; j++){
for(int k=j; k<j+i; k++)
dp[j][j+i] = min(dp[j][k] + dp[k+1][j+i], dp[j][j+i]);
dp[j][j+i] += cc[j+i]-cc[j-1];
}
}
cout << dp[1][n] << endl;
}
敘述:給兩字串求最長共同子序列
關鍵:DP…
範例程式碼:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=5001;
string s1, s2;
int dp[maxn][maxn];
int main() {
int l1, l2;
while (cin >> s1 >> s2) {
l1 = (int)s1.length();
l2 = (int)s2.length();
memset(dp, 0, sizeof(dp));
for (int i=1; i<=l1; i++) {
for (int j=1; j<=l2; j++) {
if (s1[i-1] == s2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
}
}
}
cout << dp[l1][l2] << endl;
}
return 0;
}
利用於以排序資料
lower_bound是取大於等於,upper_bound必然是大於
範例CODE:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n;
while(cin>>n){
vector <int> up;
for(int i=0;i<n;i++){
int a;
cin>>a;
up.push_back(a);
}
sort(up.begin(),up.end());
int find;
cin>>find;
int ans=upper_bound(up.begin(),up.end(),find)-up.begin();//減去begin()是為了取得vector中的位置
cout<< ans <<endl;
}
}
主要利用在想暴力解時的優化方式
類似遊戲終極密碼的概念
利用某個chk函數去確認是否符合答案
範例CODE:
while(r - l > 1) {
int m = (l + r)/2;
if(chk(m) == true) r = m;
else l = m;
}
以為這樣就結束了嗎?NoNoNo
其實是要看情況去撰寫、重新定義上下界的
這是apcs基地台問題我的AC CODE
#include<bits/stdc++.h>
using namespace std;
#define wh_ale ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define int long long
int n, k;
int a[200001];
bool chk(int x) {
int i, j, cnt=0, e=-1;
for (i = 0; i < n; i++) {
if (a[i] > e) {
cnt++;
e = a[i] + x;
}
}
if (cnt <= k) return true;
else return false;
}
signed main() {
wh_ale;
cin >> n >> k;
int i, j;
for (i = 0; i < n; i++) {
cin >> a[i];
}
sort(a, a + n);
int l = 0, r = a[n - 1], mid;
while (l < r) {
mid = l + (r - l) / 2;
if (chk(mid) == true) {
r = mid;
}
else l = mid + 1;
}
cout << l << '\n';
return 0;
}