开干
1001
思路:
就是扫描找到合法第一个然后就能去更新了
细节还是很好玩的
代码:
/*
对称:由于矩阵扫描方式是完全对称的,所以结果也会相同
扫描思想:
枚举行,枚举列
对于每一行,去看看前1...j列是不是出现了K种数字
如果出现了k种,知道了这些数字的最小出现列的最大列,就是Y的最大可满足位置
这样对于每一行就拿到了合法的Y的总数
然后,再将所有行给统计就是答案了
注意,因为这里是(1,1) -> (x,y) 所以之前统计的种类数,是可以直接使用的
因此需要维护全局seen,就实现了要求,一旦某一行出现seen == k,那么之后的所有行都能开始更新ans了
f[t]:数字t出现的最左列
*/
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
int T;
for(cin>>T;T;T--){
int N, M, K;
cin >> N >> M >> K;
vector<vector<int>> a(N+1, vector<int>(M+1));
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= M; j++)
{
cin>>a[i][j];
}
}
int seen = 0;
ll ans = 0;
vector<int> f(K+1, M+1);
for(int i = 1; i <= N; i++)
{
for(int j = 1,t; j <= M; j++)
{
t = a[i][j];
if(f[t]>j)
{
if(f[t] == M+1) seen++;
f[t] = j;
}
}
if(seen < K) continue;
int ymin = 0;
for(int t = 1; t <= K; t++) ymin = max(ymin, f[t]);
ans += (M-ymin+1);
}
cout << ans << '\n';
}
return 0;
}
1002
思路:
【01背包】
选或不选,选哪些前缀?
重点(我的思维盲点):每次选只能选前缀,而且每次分配的背包大小都不固定,所以要最后枚举背包容量
定义dp[i][j]:前i道题目使用容量k的背包选择的最大次数
转移:先i,再j,最后k,01背包经典转移就完了
其实这个题目我脑子里面是前后两条线傻傻分不清楚,但是其实就是枚举背包容量再01背包选就行了,因为每次都必须选前缀,所以也只有前缀和的1次选取就行,没有花里胡哨的
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 105, MAXM = 5e3 + 10;
ll dp[MAXN][MAXM], ans, tot;
template <typename T> bool chkmin(T &x, T y) {return y < x ? x = y, 1 : 0;}
template <typename T> bool chkmax(T &x, T y) {return y > x ? x = y, 1 : 0;}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
int T, N, M, X;
for(cin >> T; T; --T){
cin >> N >> M >> X;
vector<vector<ll>> S(N+1, vector<ll>(M+1)), A = S, preS = S, preA = S;
tot = 0;
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= M; ++j)
{
cin >> S[i][j] >> A[i][j];
preS[i][j] = preS[i][j-1] + S[i][j];
preA[i][j] = preA[i][j-1] + A[i][j];
tot += A[i][j];
}
}
memset(dp, 0, sizeof dp);
ans = 0;
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= M; ++j)
{
for(int k = 0; k <= X;++k)
{
//初始化,压缩了一维,降维了,滚动数组
chkmax(dp[i][k], dp[i-1][k]);
if(k>=preS[i][j]) chkmax(dp[i][k], dp[i-1][k - preS[i][j]]+preA[i][j]);
chkmax(ans,dp[i][k]);
}
}
}
cout << tot - ans << '\n';
}
return 0;
}
1003
这个题目竟然没想出来,基础非常薄弱啊,连bfs网络图最短路都想不起来了
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500 + 10;
int T, N, M, a[MAXN][MAXN], k, val;
bool vis[MAXN][MAXN];
int dx[4]{0,0,1,-1}, dy[4]{1,-1,0,0};
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
for(cin >> T; T; T--){
memset(a,0,sizeof a);
memset(vis,0,sizeof vis);
cin >> N >> M;
for(int i = 1;i <= N; i++)
{
cin >> k;
for(int j = 0; j < k; j++)
{
cin >> val;
a[i][val] = 1;
}
}
queue<tuple<int,int,int>> q;
for(int i = 1; i <= N; i++)
if(!a[i][1]) q.push({i,1,1});
while(!q.empty())
{
auto [x,y,d] = q.front();
q.pop();
if(vis[x][y]) continue;
vis[x][y] = true;
if(y == M){
cout << d << '\n';
break;
}
for(int k = 0; k < 4; ++k)
{
int nx = x + dx[k], ny = y + dy[k];
if(nx<1||nx>N||ny<1||ny>M||a[nx][ny]||vis[nx][ny]) continue;
q.push({nx,ny,d+1});
}
}
}
return 0;
}
1004
1005
思路:
将B中所有长度为n的字符串建字典树
然后在树上看能不能保留1就完了
但是炸了,所以另谋他法...
就是题解的暴力了?
让GPT实现了我的思路,不过是SAM,后缀数组...
我就直接挂了(又偷懒了)
代码:
#include <bits/stdc++.h>
using namespace std;
struct SAM_Node {
int nxt[2]{-1, -1}; // 出边,-1 表示不存在
int link = -1; // 后缀链接
int len = 0; // 本状态能表示的最长子串长度
int dp = 0; // 从该结点继续能再走多长(向下最长链)
};
struct SuffixAutomaton {
vector<SAM_Node> st;
int last; // 末尾状态
explicit SuffixAutomaton(int maxLen = 1) { init(maxLen); }
void init(int maxLen) {
st.clear();
st.reserve(maxLen * 2 + 2);
st.push_back(SAM_Node()); // root = 0
last = 0;
}
void extend(int c) { // c ∈ {0,1}
int cur = st.size();
st.push_back(SAM_Node());
st[cur].len = st[last].len + 1;
int p = last;
while (p != -1 && st[p].nxt[c] == -1) {
st[p].nxt[c] = cur;
p = st[p].link;
}
if (p == -1) {
st[cur].link = 0;
} else {
int q = st[p].nxt[c];
if (st[p].len + 1 == st[q].len) {
st[cur].link = q;
} else {
int clone = st.size();
st.push_back(st[q]); // 复制 q
st[clone].len = st[p].len + 1;
while (p != -1 && st[p].nxt[c] == q) {
st[p].nxt[c] = clone;
p = st[p].link;
}
st[q].link = st[cur].link = clone;
}
}
last = cur;
}
/* 计算每个结点还能再往下延伸的最长长度 */
void build_dp() {
int sz = st.size();
vector<int> bucket; bucket.reserve(sz);
for (int i = 0; i < sz; ++i) bucket.push_back(i);
sort(bucket.begin(), bucket.end(),
[this](int a, int b) { return st[a].len < st[b].len; });
for (int idx = sz - 1; idx >= 0; --idx) {
int v = bucket[idx];
int best = 0;
for (int c = 0; c < 2; ++c) {
int to = st[v].nxt[c];
if (to != -1) best = max(best, st[to].dp + 1);
}
st[v].dp = best;
}
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int N, M;
string A, B;
cin >> N >> M >> A >> B;
/* ---------- 1. 建 SAM ---------- */
SuffixAutomaton sam(M);
for (char ch : B) sam.extend(ch - '0');
sam.build_dp();
/* ---------- 2. 贪心走最大 xor ---------- */
int v = 0; // 当前 SAM 结点
int rem = N; // 还剩多少位没走
long long ones = 0; // 结果 1 的个数
for (int i = 0; i < N; ++i) {
int abit = A[i] - '0';
int want = abit ^ 1; // 想把这一位变成 1
int to_w = sam.st[v].nxt[want];
if (to_w != -1 && sam.st[to_w].dp >= rem - 1) {
v = to_w; // 可以,直接异或成 1
++ones;
} else {
int to_s = sam.st[v].nxt[abit];
v = to_s; // 只能保持为 0
}
--rem;
}
cout << ones << '\n';
}
return 0;
}

浙公网安备 33010602011771号