キャンプアルゴリズムブラッシング記録2

503 AB ペア
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <map>
using namespace std;
map<int, int>mp;
int main(void) {
    
    
	int n, c, x;
	cin >> n >> c;
	for (int i = 1; i <= n; ++i) {
    
    
		cin >> x;
		mp[x]++;
	}
	int ans = 0;
	for (auto t : mp) {
    
    
		ans = ans + t.second * mp[t.first - c];
	}
	cout << ans;
	return 0;
}
504 デジタルコンピューティング
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <map>
using namespace std;
typedef long long  ll;
const int mod = 998244353;
ll qmul(ll x, ll y) {
    
       //返回x*y模mod后的值。转化成二进制加法(快速加)
	ll ret = 0;
	while (y) {
    
    
		if (y & 1)    //二进制下,若y末尾为1
			ret = (ret + x) % mod;    //则加x*1,并将x左移1位。
		x = (x << 1) % mod;    //若y末尾为0,则加x*0,并将x左移1位,即直接左移。
		y = y >> 1;    //准备计算y前一位的值
	}
	return ret;
}
int main(void) {
    
    
	ll n,i;
	int t = 10;
		cin >> n;
		if (n < 10) {
    
    
			cout << n;
			return 0;
		}
		ll ans = 0;
		for (i = 10; i <= n; i = i * 10) {
    
    
			//ans = (ans + ((1 + i - i / 10) % mod) * ((i - i / 10) % mod) / 2) % mod;
			if ((1 + i - i / 10) % 2 == 0)
				ans = ans + qmul((1 + i - i / 10) / 2, i - i / 10);
			else ans = ans + qmul(1 + i - i / 10, (i - i / 10) / 2);
		}
		//ans = ans + ((1 + (n - i / 10 + 1))%mod) * ((n - i / 10 + 1)%mod) / 2;
		if((1 + n - i / 10+1)%2==0)
			ans = ans + qmul((1 + n - i / 10+1) / 2, n - i / 10 + 1);
		else ans = ans + qmul(1 + n - i / 10+1 , (n - i / 10 + 1)/2);
		cout << ans%mod<<"\n";
	
	return 0;
}

2 つの大きな数値の法を取得できるはずです。最初にそれぞれの法を取得し、次に乗算してから法を取得します。上記でコメントアウトされたコードのように記述した場合も ac になります。

602 01 シーケンス (良い質問)
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <map>
using namespace std;
const int mod = 1e9+7;
const int N = 1e6 + 10;
int dp[N][2];//代表第i位是0或1的个数
int n, k;
signed main(void) {
    
    
	
	cin >> n >> k;
	dp[0][0] = 1;
	//dp[0][1] = 1;
	for (int i = 1; i <= n; ++i) {
    
    
		dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
		dp[i][1] = (dp[i - k - 1 > 0 ? i-k-1 : 0][0] + dp[i - k - 1 > 0 ? i-k-1 : 0][1]) % mod;
	}
	cout << (dp[n][0] + dp[n][1]) % mod;
	
	return 0;
}

i 番目の桁が 0 で埋められていると仮定すると、前の桁が 0 か 1 かは関係ありません。

i 番目に 1 が入っているとすると、少なくとも ik 番目から i-1 番目までは 0 でなければなりませんが、ik-1 番目が 0 であれば影響はありません。

つまりdp [ i ] [ 1 ] = dp [ i − k − 1 ] [ 0 ] + dp [ i − k − 1 ] [ 1 ] dp[i][1]= dp[ik-1][0]+ dp[ik-1][1]d p [ i ] [ 1 ]=d p [ ik1 ] [ 0 ]+d p [ ik1 ] [ 1 ] なぜ、次の数字はすべて 0 でなければならないため、

初期状態はdp [0] [0] = 1 dp[0][0]=1 に設定されています。d p [ 0 ] [ 0 ]=1 (なぜ、なぜdp [ 0 ] [ 1 ] dp[0][1]d p [ 0 ] [ 1 ] は1 に設定しないでください。例を示します)

次に、ik-1 が 0 未満の場合、デフォルトでdp [0] [0] dp[0][0]になることを考慮します。dp [ 0 ] [ 0 ]転送_なぜ???

601 BFSの練習
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <map>
#include <cstring>
#include <queue>
using namespace std;
queue<int>q;
const int mod = 1e9+7;
const int N = 1e6 + 10;
int dis[N];
int ch[N];
void bfs(int v, int u) {
    
    
	if (v >= 1 && v <= 1e5 && dis[v] == 0x3f3f3f3f) {
    
    
		dis[v] = dis[u] + 1;
		q.push(v);
	}
}
signed main(void) {
    
    
	int a, x;
	cin >> a >> x;
	memset(dis, 0x3f, sizeof(dis));
	dis[a] = 0;
	for (int i = 1; i <= x; ++i) cin >> ch[i];
	q.push(a);
	while (!q.empty()) {
    
    
		int u = q.front();
		bfs(u+1, u);
		bfs(u * 2, u);
		bfs(u * 3, u);
		bfs(u - 1, u);
		q.pop();
	}
	for (int i = 1; i <= x; ++i) {
    
    
		cout << dis[ch[i]] << " ";
	}
	return 0;
}

初期値はこの数字が出現したかどうかを判定するための最大値に設定されており、出現した場合はその前に出現した数字の方が小さいはずなので操作を続行する必要はありません。

2回目は自分で作りました:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <map>
#include <queue>
using namespace std;
const int N = 2e6 + 10;
int ch[N];
int vis[N], ans[N];
queue<int>x;
map<int, int>mp;
int main(void) {
    
    
	int a, q;
	cin >> a >> q;
	for (int i = 1; i <= q; ++i) {
    
    
		scanf("%d", &ch[i]);
		mp[ch[i]] = 1;
	}
	int cnt = 0;
	x.push(a);
	vis[a] = 1;
	ans[a] = 0;
	if (mp[a] == 1) cnt++;
	while (!x.empty()) {
    
    
		int n = x.front();
		x.pop();
		if (n > 1e5) continue;
		if (n + 1 <= 1e5) {
    
    
			if (vis[n + 1] == 0) {
    
    
				x.push(n + 1);
				ans[n + 1] = ans[n] + 1;
				vis[n + 1] = 1;
				if (mp[n + 1] == 1) cnt++;
			}
		}
		if (n * 2 <= 1e5) {
    
    
			if (vis[n * 2] == 0) {
    
    
				x.push(n * 2);
				ans[n * 2] = ans[n] + 1;
				vis[n * 2] = 1;
				if (mp[n * 2] == 1) cnt++;
			}
		}
		if (n * 3 <= 1e5) {
    
    
			if (vis[n * 3] == 0) {
    
    
				x.push(n * 3);
				ans[n * 3] = ans[n] + 1;
				vis[n * 3] = 1;
				if (mp[n * 3] == 1) cnt++;
			}
		}
		if (n - 1 <= 1e5&&n-1>=0) {
    
    //不特判会RE
			if (vis[n - 1] == 0) {
    
    
				x.push(n - 1);
				ans[n - 1] = ans[n] + 1;
				vis[n - 1] = 1;
				if (mp[n - 1] == 1) cnt++;
			}
		}
		if (cnt >= q) break;//如果都出现过了就break呗,但是为什么比上面的要慢啊
	}
	for (int i = 1; i <= q; ++i) {
    
    
		printf("%d ", ans[ch[i]]);
	}
	return 0;
}
割り切れる独身者
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <map>
#include <cstring>
#include <queue>
using namespace std;
signed main(void) {
    
    
	int x;
	cin >> x;
	int i = 1;
	int ans = 1;
	int first = 1;
	if (x == 1) {
    
    
		cout << 1 << " " << 1;
		return 0;
	}
	while (1) {
    
    
		if (i % x != 0) {
    
    
			i = (i % x) * 10 + 1;
			ans++;
			if (i / x == 0) {
    
    
				if (!first) cout << 0;
			}
			else {
    
    
				cout << i / x;
				first = 0;//不能输出前导0
			}
		}
		else break;
	}
	cout << " " << ans;
	
	return 0;
}

アイデアは、条件が満たされているかどうかを判断するために 1 の数を継続的に増加させることです。縦割りに似た精度の高い質問、最後に1を追加

507 ルリスのゲーム
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int ch[N],n;
int check(int mid) {
    
    
	int init = mid;
	for (int i = 1; i <= n; ++i) {
    
    
		if (ch[i] > init) init = init - (ch[i] - init);
		else init = init + (init - ch[i]);
		if (init < 0) return 0;
		if (init > 1e5+10) return 1;//不加这一句就错了啊,因为会爆longlong
	}
	return 1;
}
int main(void) {
    
    
	int maxx=0;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
    
    
		cin >> ch[i];
		if (ch[i] > maxx) maxx = ch[i];
	}
	int l = 1, r = maxx + 1;
	while (l < r) {
    
    
		int mid = (l + r) / 2;
		//cout <<l <<" " << r<<" " << mid<<"\n";
		if (check(mid)==1) r = mid;
		else l = mid + 1;
	}
	cout << l;
	return 0;
}
506 完全数
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <map>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int ch[N];
ll fact[N], infact[N];
int a, b, m;
const int mod = 1e9 + 7;
int check(int x) {
    
    
	while (x > 0) {
    
    
		if (x % 10 == a || x % 10 == b) x = x / 10;
		else return 0;
	}
	return 1;
}
ll qpow(ll base, ll pow) {
    
    //快速幂 
	ll ans = 1;
	while (pow) {
    
    
		if (pow & 1) ans = ans * base % mod;
		base = base * base % mod;
		pow >>= 1;
	}
	return ans;
}
void init() {
    
    
	fact[0] = infact[0] = 1;
	for (int i = 1; i < N; i++) {
    
    
		//阶乘预处理,打表
		fact[i] = fact[i - 1] * i % mod;
		//逆元 i 的 mod-2次方就是 i的模 mod的逆元
		infact[i] = infact[i - 1] * qpow(i, mod - 2) % mod;
	}
}
ll c(int n, int k) {
    
    
	if (k<0 || k>n) return 0;
	return fact[n] * infact[k] % mod * infact[n - k] % mod;
}

signed main(void) {
    
    
	init();
	cin >> a >> b >> m;
	int ans = 0,cnt=0;
	if (a > b) swap(a, b);
	for (int i = 1; i <= m+1; ++i) {
    
    
		ch[i] = (b - a) * (i-1) + a*m;
		if (check(ch[i])) {
    
    
			cnt++;
			ans = (ans + c(m, i-1) ) % mod;
		}
	}
	cout << ans;
	/*cout << cnt;
	cout << "\n";
	cout << c(6,5)<<"\n"<<c(6,6);*/
	return 0;
}

合計を列挙し、条件を満たしていればその組み合わせの数を求める(組み合わせの数をボードから求める)

407 はらぺこ夏休みカーニバル
#include <iostream>
using namespace std;
int ch[200][200];
int kk[200];
int flag[200];
int main(void) {
    
    
	int n, k, x;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
    
    
		cin >> kk[i];
		for (int j = 1; j <= kk[i]; ++j) {
    
    
			cin >> ch[i][j];
		}
	}
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j =  1; j <= n; ++j) {
    
     //还是要从1开始,不能从i+1开始
			if (j == i) continue;
			int sum = 0;
			for (int k = 1; k <= kk[i]; ++k) {
    
    
				for (int l = 1; l <= kk[j]; ++l)    {
    
    
					if (ch[i][k] == ch[j][l]) {
    
    
						sum++;
						break;//要break,因为有重复的,这时继续加就会出错
					}
				}
			}
			if (sum == kk[i]) flag[j] = 1;
		}
	}
	for (int i = 1; i <= n; ++i) {
    
    
		if (flag[i]) cout << "NO\n";
		else cout << "YES\n";
	}
	return 0;
}

シミュレーションの問題は、問題の意味に従って書くだけでも、for ループの順序や範囲など、何かを得ることができます。

406 ループ部分文字列
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
string s[109];
int len[109];
int main(void) {
    
    
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
    
    
		cin >> len[i];
		cin >> s[i];
	}
	for (int i = 1; i <= n; ++i) {
    
    
		string sssss = s[i];
		//string str = s.reserve();这种写法根本没翻转
		reverse(sssss.begin(), sssss.end());
		//cout << sssss << "\n";
		int flag = 0;
		for (int j = 0; j < len[i]; ++j) {
    
    
			//cout << s[i].substr(len[i] - j) << " " << s[i].substr(0, len[i] - j) << "\n";
			if((s[i].substr(len[i] - j)+s[i].substr(0,len[i]-j))==sssss) flag=1;
		}
		if(flag) cout<<"YES\n";
		else cout<<"NO\n";
	}
	return 0;
}

最初に変換することは、部分文字列が反転された後、循環部分文字列になることです。その後、元の文字列が反転後のループと等しい限り、

それから文字列反転の書き方を学びました

604 衝突 2
#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;
const int N = 2e5 + 10;
map<int, int>mpl;//记录第i行往左走的最大值
map<int, int>mpr;//记录第i行往右走的最小值
struct node {
    
    
	int x;
	int y;
}point[N];
//找每一行中往左走的最大值,和往右走的最小值,如果maxx>min,就会产生碰撞
int main(void) {
    
    
	int n;
	string str;
	cin >> n;
	for (int i = 0; i < n; ++i) {
    
    
		cin >> point[i].x >> point[i].y;
		mpl[point[i].y] = -1;
		mpr[point[i].y] = 1e9;
	}
	cin >> str;
	for (int i = 0; i < n; ++i) {
    
    
		if (str[i] == 'L') {
    
    
			mpl[point[i].y] = max(point[i].x, mpl[point[i].y]);
			if (mpr[point[i].y] < point[i].x) {
    
    
				cout << "Yes";
				return 0;
			}
		}
		if (str[i] == 'R') {
    
    
			mpr[point[i].y] = min(point[i].x, mpr[point[i].y]);
			if (mpl[point[i].y] > point[i].x) {
    
    
				cout << "Yes";
				return 0;
			}
		}
	}
	cout << "No";
	return 0;
}

ある瞬間に条件が満たされた場合、リターンを直接出力できます。

もう一つの方法は、読んだ後に操作することです

#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;
const int N = 2e5 + 10;
map<int, int>mpl;//记录第i行往左走的最大值
map<int, int>mpr;//记录第i行往右走的最小值
struct node {
    
    
	int x;
	int y;
}point[N];
//找每一行中往左走的最大值,和往右走的最小值,如果maxx>min,就会产生碰撞
int main(void) {
    
    
	int n;
	string str;
	cin >> n;
	for (int i = 0; i < n; ++i) {
    
    
		cin >> point[i].x >> point[i].y;
		mpl[point[i].y] = -1;
		mpr[point[i].y] = 1e9;
	}
	cin >> str;
	for (int i = 0; i < n; ++i) {
    
    
		if (str[i] == 'L') mpl[point[i].y] = max(mpl[point[i].y], point[i].x);
		else mpr[point[i].y] = min(mpr[point[i].y], point[i].x);
	}
	/*for (auto t : mpl) {
		cout << t.first << " " << t.second<<" ";
	}
	cout << "\n";
	for (auto t : mpr) {
		cout << t.first << " " << t.second<<" ";
	}*/
	for (auto t : mpr) {
    
    
		if (t.second == 1e9) continue;
		if (t.second < mpl[t.first]) {
    
    
			cout << "Yes";
			return 0;
		}
	}
	cout << "No";
	return 0;
}

将来マップループを使用するときは、最初に答えを出力して確認することをお勧めします。ここでは、0から始まり、最大のものが表示されるまで、つまり、表示されないものも表示されることがわかります。 for ループ内で

606 巨大ブルペン

動的計画法演習:伝達方程式を理解するのは難しい

#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;
const int N = 2e3 + 10;
int ch[N][N];
int dp[N][N];
int main(void) {
    
    
	int n, t;
	int x, y;
	cin >> n >> t;
	for (int i = 1; i <= t; ++i) {
    
    
		cin >> x >> y;
		ch[x][y] = 1;
	}
	int maxx = 0;
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= n; ++j) {
    
    
			if (ch[i][j] == 0) {
    
    
				dp[i][j] = min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1]))+1;
				if (dp[i][j] > maxx) maxx = dp[i][j];
			}
		}
	}
	cout << maxx;
	return 0;
}

同様の質問

羅鼓1387最大の広場

0 と 1 を含む行列を入力してください

1 の最大の正方形を見つける

解決策: 接頭辞の合計 + 辺の長さの乱暴な列挙

最適化: 大きいものから小さいものまで列挙する

#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;
const int N = 2e3 + 10;
int ch[N][N];
int cnt[N][N];
int main(void) {
    
    
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= m; ++j) {
    
    
			cin >> ch[i][j];
			cnt[i][j] = cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1] + ch[i][j];//前缀和
		}
	}
	int ans = 1;
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= m; ++j) {
    
    
			for (int k = ans; k <= min(n, m); ++k) {
    
    //为什么这里是从小到大枚举,而下面的暴力是从大到小枚举,因为如果这里从大到小枚举的话,最大的满足不了,跳出循环,就找下一个点去了,而下面暴力解法的跳出循环是跳到k-1
				if (i + k > n || j + k > m || (cnt[i + k][j + k]-cnt[i-1][j+k]-cnt[i+k][j-1]+cnt[i-1][j-1] != ((k+1)*(k+1)))) break;
				ans = max(ans, k+1);
			}
		}
	}
	cout << ans;
	return 0;
}









#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
using namespace std;
int ch[101][101];
int cnt[1010][1010];
int main(void) {
    
    
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= m; ++j) {
    
    
			scanf("%d", &ch[i][j]);
			cnt[i][j] = cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1] + ch[i][j];
		}
	}
	int ans = 1;
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= m; ++j) {
    
    
			for (int k = min(n,m); k >=ans; --k) {
    
    
				if (i + k > n || j + k > m) continue;//如果从大到小枚举的话,应该是相等跳出循环
				if (i + k <= n&& j + k <= m && (cnt[i + k][j + k] - cnt[i - 1][j + k] - cnt[i + k][j - 1] + cnt[i - 1][j - 1]) == ((k + 1) * (k + 1))) {
    
    
					ans = k + 1;
					break;
				}
			}
		}
	}
	cout << ans;
	return 0;
}

ブルートフォースソリューション

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
using namespace std;
int ch[101][101];
int main(void) {
    
    
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= m; ++j) {
    
    
			scanf("%d", &ch[i][j]);
		}
	}
	int ans = 1;
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= m; ++j) {
    
    
			for (int k = min(n, m); k >= ans; --k) {
    
    
				int p = 0;
				for (int x = i; x < i + k; ++x) {
    
    
					for (int y = j; y < j + k; ++y) {
    
    
						if (x>n||y>m||ch[x][y] == 0) p = 1;
						if (p == 1) break;
					}
					if (p == 1) break;
				}
				if (p == 0) ans = k;
			}
		}
	}
	cout << ans;
	return 0;
}
607 高利貸し
#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;
const int N = 2e3 + 10;
int ch[N][N];
int cnt[N][N];
const double eps = 1e-8;
int n, m, k;
double check(double mid) {
    
    
	double sum = 0;
	for (int i = 1; i <= k; ++i) {
    
    
		sum = sum + m / pow(1 + mid, i);
	}
	if (sum >= n) return 1;
	return 0;
}
int main(void) {
    
    
	cin >> n >> m >> k;
	double l = 0, r = 5;
	for (int i = 1; i <= 100; ++i) {
    
    
		double mid = (l + r) / 2;
		if (check(mid)) l= mid;
		else r = mid;
	}
	//cout << l;
	printf("%.6f", l);//卡精度的题看来得用printf卡小数点位数,反正我cout过不了
	return 0;
}
701 バックパック
#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int ch[N];
int n, m, k;
signed main(void) {
    
    
	int t, n, w;
	int check;
	cin >> t;
	while (t--) {
    
    
		cin >> n >> w;
		if (w % 2 == 0) check = w / 2;
		else check = (w + 1) / 2;
		int flag = 0;
		for (int i = 1; i <= n; ++i) {
    
    
			cin >> ch[i];
			if (ch[i] >= check && ch[i] <= w) flag = 1;
		}
		if (flag) {
    
    
			cout << "YES" << "\n";
			continue;
		}
		sort(ch + 1, ch + 1 + n);
		int sum = 0;
        //for循环从前往后,或者从后往前都可以
		for (int i = n; i >=1; --i) {
    
    
			sum += ch[i];
			if (sum > w) sum -= ch[i];
			else if (sum >= check) {
    
    
				flag = 1;
				break;
			}
		}
		if (flag) cout << "YES\n";
		else cout << "NO" << "\n";
	}
	return 0;
}

ここで明確にする必要があるのは、それは不連続ではなく連続的でなければならないということです。つまり、123 が w を超えても 13 が満たされるという状況は存在しません。123 が超えた場合、12 が満たされなければならないことを意味します。なぜなら、それぞれの値が異なるからです。この時の項目 音量がw/2未満です(音量>w/2&&<wの項目があった場合はそのままyesが出力されるため)

703 単純な XOR 問題
#include <iostream>
#include <cmath>
using namespace std;
#define int long long
signed main(void) {
    
    
	int n, m;
	cin >> n >> m;
	if (m == 1) {
    
    
		if (n == 1) {
    
    
			cout << 2;
			return 0;
		}
		else {
    
    
			cout << 1;
			return 0;
		}
	}
	int ans = pow(2, m);//直接cout<<pow(2,n)不行,例如n=20,输出1.04858e+06
	if (n == 0) cout << ans;
	else cout << ans - 1;
	return 0;
}

1 つの結論は、0 から2 n − 1 2^n-1であるということです。2n1の XOR 合計は 0 (n が 1 でない場合) です。x を取得したい場合は、XOR 合計が 1 のときは x を取得しないでください。

m XOR x = 0の場合m XOR x = 0エム・エックス・オー・エックス=0の場合、m=x

なぜこのようなことを考えるかというと、質問は最も要求が厳しいため、すべてを選択するとどうなるかを考える必要があり、この数字はかなり特別で、2 の平方数について考えなければなりません。

605綺麗ですね!最長の昇順サブシーケンス
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 1e6 + 10;
int dp[N];
int ch[N];
int main(void) {
    
    
	int t, n;
	cin >> t;
	while (t--) {
    
    
		cin >> n;
		memset(ch, 0, sizeof(ch));
		memset(dp, 0, sizeof(dp));
		int maxx = 0;
		for (int i = 1; i <= n; ++i) cin >> ch[i];
		for (int i = 1; i <= n; ++i) {
    
    
			maxx = max(dp[i], maxx);
			for (int j = i + i; j <= n; j = j + i) {
    
    
				if (ch[j] > ch[i]) dp[j] = max(dp[j], dp[i] + 1);
			}
		}
		cout << maxx + 1 << "\n";
	}
	return 0;
}

通常の dp とは異なり、最長の昇順部分列を見つけるには i を列挙します。for int j=1;j<i;++j

706 神のコレクション(良い質問)

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define int long long
priority_queue<int, vector<int>,greater<int> >q;//定义的方法我忘了寄
//还有一种方法就是还是大根堆,但是push的时候把他变成负数是不是就可以了,这里应该不太行,因为后面涉及到加减法相关的操作
signed main(void) {
    
    
	int n;
	int x,y;
	int tag = 0;
	scanf("%lld",&n);
	while (n--) {
    
    
		scanf("%lld", &x);
		if (x == 1) {
    
    
			scanf("%lld", &y);
			q.push(y - tag);//保持相对的大小关系!!!!!!!!
		}
		else if (x == 2) {
    
    
			scanf("%lld", &y);
			tag += y;
		}
		else {
    
    
			int u = q.top();
			q.pop();
			printf("%lld\n", u + tag);
		}
	}
	return 0;
}
定义大根堆的方法需要记一下的,还有就是全部元素加上一个数,如果循环逐个加的话肯定会炸,所以定义一个tag来存储现在加了多少,但是新插入一个数时push的时候要减去当前的tag,输出的时候要加上tag
    还有就是#define int long long 然后scanf里面要用lld别忘了
704 部分文字列の円運動
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
signed main(void) {
    
    
	int n;
	int l, r, k;
	string str;
	cin >> str;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
    
    
		cin >> l >> r >> k;
		k = k % (r - l + 1);
		if (k == 0) continue;
		str = str.substr(0, l - 1) + str.substr(r - k, k) + str.substr(l - 1, r-l + 1 - k) + str.substr(r);
		}
		cout << str;

	return 0;
}

簡単なシミュレーションの質問

鉱山を掘る

マップ上には N 個の地下室 (N<=20) があり、各地下室には一定数の地雷が埋められています。同時にセラー間の接続経路も指定されます。

地下室のデータとその接続が与えられると、任意の場所から鉱山を掘り始め、指定された接続に沿って掘り下げ(選択できるパスは 1 つだけ)、接続がなくなると鉱山を掘る作業は終了します。 。誰かがより多くの地雷を掘ることができるように、地雷を掘る計画を立てます。

W1 W2 W3…WN (各地下室に埋められた地雷の数を示します)

A12……………….A1N(セラー間の接続経路、Aij=1はセラーiとjの間に通路があるかどうかを示す:Aij=1で通信、Aij==0で通信なし)

A23…………A2N

…………AN-1N

#include <iostream>
using namespace std;
#define int long long 
int a[101];
bool check[111];
int n;
int maxx;
bool f[111][111];
bool chck(int x)
{
    
    
	for (int i = 1; i <= n; i++)
	{
    
    
		if (f[x][i] && !check[i]) return false;
	}
	return true;
}
void dfs(int x, int sum)
{
    
    
	if (chck(x))
	{
    
    
		if (maxx < sum)
		{
    
    
			maxx = sum;
		}
		return;
	}
	for (int i = 1; i <= n; i++)
	{
    
    
		if (f[x][i] && !check[i])
		{
    
    
			check[i] = 1;
			dfs(i, sum + a[i]);
			check[i] = 0;//别忘了回溯。。。。。
		}
	}
}
signed main()
{
    
    
	cin >> n;
	for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
	for (int i = 1; i < n; i++)
	{
    
    
		for (int j = i + 1; j <= n; ++j) {
    
    
			scanf("%lld", &f[i][j]);
			f[j][i] = f[i][j];//双向边
		}
	}
	for (int i = 1; i <= n; i++)
	{
    
    
		check[i] = 1;
		dfs(i, a[i]);
		check[i] = 0;
	}
	cout << maxx;
	return 0;
}

Guess you like

Origin blog.csdn.net/Aure219/article/details/131956166