2022 Aug 18 刷题log

        CF 811D  Color with Occurrences

问:给一个string s,和 n个substring ai,求最少数量的substring覆盖s

转换字符串覆盖问题 -> 区间覆盖问题

区间覆盖

        1. 按左边界升序排

        2. 在 左边界 小于/等于/等于+1 当前右边界里,选右边界最远的

当s的长度短,可以使用 数组 做区间覆盖

        ri 代表从 si开始可以覆盖到的最远右边界

规定左右边界协议 和 下标

        左闭右开 or 左闭右闭,0-index or 1-index

学习

        1.  s.substr(开始, substr长度)

        2.s.length() 要 cast 到 int,不然会 RE

        3. max_element()  左闭右开                        

总结

          1. 转换字符串覆盖问题 -> 区间覆盖问题

          2.  pair区间覆盖 和 数组区间覆盖   

          3.  规定左右边界 和 下标协议

#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
#include <utility>
using namespace std;
 
/* 
1
bababa
2
ba
aba
*/
void solve(){
	string s; cin>>s;
	int n; cin>>n;
	//0 index, 左闭右开
	int r[105]; 
	int id[105];
	memset(r, 0, sizeof(r));
	for(int i=0;i<n;i++){
		string sb; cin>>sb;
		//应该是 小于等于
		for(int j=0;j<=(int)s.length()-(int)sb.length();j++){
			//substr(开始, substr的长度)
			if(s.substr(j, sb.length())==sb){
				//开始 + 长度 超 1
				//r[j] in
				//左闭右闭 [l, r]
				if(j+sb.length() > r[j]){
					r[j]=j+sb.length();
					id[j]=i;
				}
			}
		}
	}
	// for(int i=0;i<s.length();i++){
	// 	cout<<r[i]<<" ";
	// }
	int end=0;
	vector<pair<int, int> > ans;
	while(end<s.length()) {
		//max_element 左闭右开 [r, l)
		int next_end=max_element(r, r+end+1)-r;
		if(r[next_end] <= end){ 
			cout<<-1<<endl;
			return;
		}
		ans.push_back({id[next_end], next_end});
		end=r[next_end];
	}	
	cout<<ans.size()<<endl;
	for(int i=0;i<ans.size();i++){
		cout<<ans[i].first+1<<" "<<ans[i].second+1<<endl;
	}	
}
int main(){
	int t; cin>>t;
	while(t--){
		solve();
	}
}

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

CF1637D Yet Another Minimization Problem

给两个数组a和b,定义一个数组的cost为       

                                                        ​​​​​​​       \sum_{i=1}^{n}\sum_{j=i+1}^{n}(a_i+a_j)^2

每次操作可以选一个下标 k,交换 ak 和 bk

求两个数组的最小cost和

遇到一个公式,尝试分解到线性:

                1. 展开式子

                                        \sum_{i=1}^{n}\sum_{j=i+1}^{n}(a_i+a_j)^2                                                 2. 分离常数(constant)

                                                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                ​​​​​​​\sum_{i=1}^{n}\sum_{j=i+1}^{n}(a_i^2+2a_ia_j+a_j^2) = \sum_{i=1}^{n}\sum_{j=i+1}^{n}a_i^2 + \sum_{i=1}^{n}\sum_{j=i+1}^{n}2a_ia_j+\sum_{i=1}^{n}\sum_{j=i+1}^{n}a_j^2

                这里把sigma 分配,发现第一项和第三项是常数  (不论怎么换,a和b的都会计算)                                      

                3. 转换成线性

                                       \sum_{i=1}^{n}\sum_{j=i+1}^{n}a_i^2 = \sum_{i=1}^{n}(n-i)a_i^2

                                       \sum_{i=1}^{n}\sum_{j=i+1}^{n}a_j^2 = \sum_{j=1}^{n}(j-1)a_j^2 \rightarrow \sum_{i=1}^{n}(i-1)a_i^2

                      !!!这里注意 j 可以 转成 i

                 4. 找到不能被分解的式子,要优化(设有多个未知数)

                                        \sum_{i=1}^{n}\sum_{j=i+1}^{n}2a_ia_j

                  5. 分类合并,简化式子

                                线性合并一起,平方合并一起

                                        \sum_{i=1}^{n}(n-i)a_i^2+\sum_{i=1}^{n}(i-1)a_i^2= (n-1)\sum_{i=1}^{n}a_i^2

                    以下为完整公式:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        (n-1)(\sum_{i=1}^{n}a_i^2+\sum_{i=1}^{n}b_i^2)+2\sum_{i=1}^{n}\sum_{j=i+1}^{n}(a_ia_j+b_ib_j)                   

               找一个值对cost的贡献  

                               对于aj, 它会与  a1 .. aj-1 的结合 并产生贡献, (a1 * aj) + (a2 * aj) +....(aj-1 *aj)

目前的决策(01, 换或不换)依赖于过去的线性和 与 结果 -->  dp

        由于ai 和 bi 最大100,n最大100,于是 和 最大 20000

不论怎么换ai和bi,前i个a和b的线性和都是常数

01背包-恰好装满j的最小值,

让S[i] 等于a和b的前i个值的和,

让 dp[i][j] 等于 a的前i个值和为j 的最小cost:

       if (j >= a[i]) : 

        ​​​​​​​     dp[i][j]=min(dp[i][j], dp[i-1][j-a[i]]+(j-a[i])*a[i]+(S[i]-j-b[i])*b[i])   ​​​​​​​            

        if (j >= b[i]) :

                dp[i][j]=min(dp[i][j], dp[i-1][j-b[i]]+(j-b[i])*b[i]+(S[i]-j-a[i])*a[i])

总结

        1. 遇到一个公式,尝试分解到线性:

                1. 展开式子

                2. 分离常数(constant)

                3. 分类合并,简化式子

               4. 找到不能被分解的式子,要优化(设有多个未知数)

         

       2. 一个下标的贡献?

        动态规划?-> 决策

                       

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
 
int MAX=0x7f7f7f7f;
/* 
1
4
3 6 6 6
2 7 4 1
*/
int dp[105][20005];
void solve(){
	int n, a[105], b[105];
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	int fConst=0, S[105];
	S[0]=0;
	for(int i=1;i<=n;i++){
		fConst += a[i]*a[i]+b[i]*b[i];
		S[i]=S[i-1]+a[i]+b[i];
	}
	fConst *= (n-1);
	//memset(arr, (int)#, size)
	memset(dp, MAX, sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=n;i++){

		// j 不能!!!从 a[i] 开始
		for(int j=0;j<=S[n];j++){
			if(j>=a[i]){
				dp[i][j]=min(dp[i][j], dp[i-1][j-a[i]]+(j-a[i])*a[i]+(S[i]-j-b[i])*b[i]);
			}
			if(j>=b[i]){
				dp[i][j]=min(dp[i][j], dp[i-1][j-b[i]]+(j-b[i])*b[i]+(S[i]-j-a[i])*a[i]);
			}
		}
	}
	int ans=1e9;
	for(int j=0;j<=S[n];j++){
		ans=min(ans, dp[n][j]);
	}
	cout<<2*ans + fConst<<endl;
}
 
int main(){
	int t; cin>>t;
	while(t--){
		solve();
	}
}

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

CF 811E Add Modulo 10

        给一个数组a,每次操作可以选 i,把a[i] 变成 a[i] + (a[i] mod 10), 求能否把 所有 ai 变成一样。

                

       通过举例观察到规律 (性质)

                个位会一直在2, 4, 6, 8 循环,之后证明了有于odd+odd=even,even+even=even(除了5),于是每个同阶数的个位都是独特的,比如说 96,十位是9的只有96

        

        一开始的想法:把所有的数的个位变成2,然后打擂台,每次轮回+20 (+4+8+6+2)

        正确做法:直接每个数 % 20 就行,会等于 2 or 12,如果 有 2 和 12 则 不可能变一样

                       打擂台 -> 比较数的性质不同 -> 每个数的性质不同

       总结:

                ps:已经非常接近答案了,需更耐心的举例 和 细致的观察性质

                通过举例观察到规律 (性质)

                打擂台 -> 比较数的性质不同 -> 每个数的性质不同                

#include <iostream>
#include <algorithm>
using namespace std;
 
/*
1
3
2 18 22
*/
void solve(){
	int n; cin>>n;
	int ar[n+5];
	for(int i=0;i<n;i++) cin>>ar[i];
	for(int i=0;i<n;i++){
		while(ar[i]%10!=0 && ar[i]%10!=2) ar[i]+=ar[i]%10;
	} 
	sort(ar, ar+n);
	if(ar[n-1]%10==0){
		for(int i=0;i<n-1;i++){
			if(ar[i]!=ar[n-1]){
				cout<<"NO"<<endl;
				return;
			}
		}
		cout<<"YES"<<endl;
		return;
	}
	for(int i=1;i<n;i++){
		if(ar[i]%20!=ar[i-1]%20){
			cout<<"NO"<<endl; return;
		}
	}
	cout<<"YES"<<endl;
}
 
int main(){
	int t; cin>>t;
	while(t--){
		solve();
	}
}

                                    

        

      


版权声明:本文为Wheudnbd原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。