钉耙编程(1)

目录

1001 循环位移

Problem Description

解析:

法二字符串哈希知识点:

代码:(还有一组用例未过,待改)

1002 星星

Problem Description

解析:

法一代码:

标准答案代码法二

1003 树 (待补)

1012 并 (待补)


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 树 (待补)

---------------------------------------------------------------------------------------------------------------------------------

1012 并 (待补)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值