目录
1001 循环位移
Problem Description
定义字符串 𝑆=𝑆0+⋯+𝑆𝑛−1S=S0+⋯+Sn−1 循环位移 𝑘k 次为 𝑆(𝑘)=𝑆𝑘mod𝑛+⋯+𝑆𝑛−1+𝑆0+⋯+𝑆(𝑘−1)mod𝑛S(k)=Skmodn+⋯+Sn−1+S0+⋯+S(k−1)modn。
定义 [𝐴]={𝐴(𝑘),𝑘∈𝑁}.[A]={A(k),k∈N}.
给出 𝑇T 组串 𝐴,𝐵A,B,询问 𝐵B 有多少个子串在 [𝐴][A] 中。
Input
第一行一个 𝑇T 表示输入组数。
接下来每行两个字符串,表示 𝐴A 和 𝐵B,保证 ∣𝐴∣≤∣𝐵∣∣A∣≤∣B∣。
保证 ∑∣𝐵∣≤1048576.∑∣B∣≤1048576.,并且字符串均由大写字母组成。
Output
输出 𝑇行,每行一个数表示答案。
解析:
法二字符串哈希知识点:
代码:(还有一组用例未过,待改)
用了两种 在字符串中 求固定长度子串的 hash值的方法。
#include<iostream>
#include<map>
#include<cstring>
#include<cstdio>
using namespace std;
typedef unsigned long long ULL;
const int P=13331,N=3000005;
ULL h[N],p[N];
char s[N];
map<ULL,int>mp;
int ans;
int main(){
freopen("D:\\1001(1).txt", "w", stdout);
int t; cin>>t;
while(t--){
int ans=0;
scanf("%s",s);
p[0]=1;h[0]=0;
int len=strlen(s);
for(int i=len;i<2*len;i++)
s[i]=s[i-len];
for(int i=0;i<2*len;i++){
p[i+1]=p[i]*P;
h[i+1]=h[i]*P+s[i];
}
for(int i=1;i<=len;i++){
ULL a=h[i+len-1]-h[i-1]*p[len];
mp[a]=1;
}
scanf("%s",s);
ULL tp=0;
int i;
for(i=0;i<len;i++){
tp=tp*P+s[i];
if(mp[tp]==1) ans++;
}
int leng=strlen(s);
for(i=1;i<=leng-len+1;i++){
tp-=p[len-1]*s[i-1];
tp*=P;
tp+=s[i+len-1];
//printf("tp%llu\n",tp);
if(mp[tp]==1) ans++;
}
printf("%d\n",ans);
}
}
--------------------------------------------------------------------------------------------------------------------------------
1002 星星
Problem Description
小 A 有 𝑛n 次获得星星的机会。
在第 𝑖i 次机会里他有如下的 55 种选择(他必须做出恰好一种选择):
-
跳过这一轮。
-
𝑎𝑖ai 的代价获得 11 颗星星。
-
𝑏𝑖bi 的代价获得 22 颗星星。
-
𝑐𝑖ci 的代价获得 33 颗星星。
-
𝑑𝑖di 的代价获得 44 颗星星。
保证 0<𝑎𝑖≤𝑏𝑖≤𝑐𝑖≤𝑑𝑖≤1090<ai≤bi≤ci≤di≤109。
他想要获得恰好 𝑘k 颗星星,但是并不知道最小代价是多少,请你帮他计算这个最小值。
Input
本题有多组数据
第一行输入数据组数 𝑇T。
对于每组数据的第一行,有两个正整数表示 𝑛,𝑘n,k。
接下来 𝑛n 行,输入四个数字 𝑎𝑖,𝑏𝑖,𝑐𝑖,𝑑𝑖ai,bi,ci,di。
1≤𝑛≤1000,0≤𝑘≤𝑛×4.1≤n≤1000,0≤k≤n×4.
满足 ∑𝑛≤100000∑n≤100000
Output
对于每组数据,输出一个数字表示这组数据的答案。
解析:
法一代码:
分组背包,每组背包中最多选一个。
#include<iostream>
#include<cstring>
using namespace std;
int main(){
//freopen("D:\\1002(1).txt", "w", stdout);
int t; cin>>t;
while(t--){
int n,v; scanf("%d%d",&n,&v);
int dp[4010];
memset(dp,0x3f,sizeof(dp));
int a,b,c,d; //1 2 3 4
scanf("%d%d%d%d",&a,&b,&c,&d);
dp[0]=0;
dp[1]=a,dp[2]=b,dp[3]=c,dp[4]=d;
for(int i=2;i<=n;i++){ //机会 物品数
scanf("%d%d%d%d",&a,&b,&c,&d);
for(int j=v;j>=1;j--){ //背包容量 转一维数组 逆向枚举
if(j>=1){
dp[j]=min(dp[j-1]+a,dp[j]);
}
if(j>=2){
dp[j]=min(dp[j-2]+b,dp[j]);
}
if(j>=3){
dp[j]=min(dp[j-3]+c,dp[j]);
}
if(j>=4){
dp[j]=min(dp[j-4]+d,dp[j]);
}
}
}
printf("%d\n",dp[v]);
}
return 0;
}
标准答案代码法二
(更优,时间复杂度更低)
#include <bits/stdc++.h>
using namespace std;
const long long INF = 1e18;
const int W = 1000;
mt19937_64 rng(time(0)); //这是一个用当前时间种子化的随机数生成器,用于在算法中进行洗牌操作
void solve() {
int n, w;
cin >> n >> w;
assert(1 <= n && n <= 1000);//断言,用于确保物品数量在合理范围内。
vector<array<int, 5>> A(n);//一个大小为 n 的数组 A,每个元素是一个长度为 5 的数组
for (auto &a : A) {
a[0] = 0;
for (int i = 1; i <= 4; i++) {
cin >> a[i];
}
}
shuffle(A.begin(), A.end(), rng);
vector<vector<long long>> dp(2, vector<long long>(w + 1, INF));
vector<vector<int>> vis(2, vector<int>(w + 1, -1)); //二维数组,大小为2,交替表示前一次和当前
int p = 1, q = 0;
dp[q][0] = 0, vis[q][0] = 0; //p当前, q前一次
for (int i = 0; i < n; i++, p ^= 1, q ^= 1) { //p ^= 1, q ^= 1 交替
auto a = A[i];
long long L = 1ll * w * (i + 1) / n - W; //1ll *:确保乘法操作使用 64 位整数,避免溢出。
long long R = 1ll * w * (i + 1) / n + W;
L = max(L, 0LL);
R = min(R, (long long)w);
for (int j = L; j <= R; j++) {
if (vis[q][j] == i) { //[q][j]数据是否在上一次更新过,为什么要这样????
for (int k = 0; k <= 4; k++) {
if (j + k <= w && dp[p][j + k] >= dp[q][j] + a[k]) {
dp[p][j + k] = dp[q][j] + a[k];
vis[p][j + k] = i + 1;
}
}
}
}
}
cout << dp[q][w] << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL); //可以集中输出
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
---------------------------------------------------------------------------------------------------------------------------------
1003 树 (待补)
---------------------------------------------------------------------------------------------------------------------------------