ZR普转提day1

A

  • CF的原题
  • 给定一个数字串,让你删掉它的一个连续子串使的剩下的没有重复元素
  • 枚举要删掉子串的起点,在起点之前必须保证没有重复元素。并把这些元素存入vector,接下来找它的终点,倒着找,如果这个元素出现了两次,肯定要删掉这个元素,终点也就找到了

C

  • DP
  • 先从暴力写挂的原因写起吧,其实对于物品的顺序已经规定好了,我们只需要考虑是不是选这个物品就好了,我们就枚举每个选还是不选。是 \(2^n\) 的。选完之后,check。考试时,是爆搜之前的排序顺序错了
  • 应该是进入时间相同的,出来时间大的在前
  • 正解的排序方法是按出来时间从小到大排序,如果出来时间相同就按进入时间大的在前。这样保证,在它前边的物品一定在它上边。
  • 我们设 \(f[i][j]\) 表示 \(i\) 这个物品及其上边的物品在任何时刻最大承重是 \(j\) 所获得的最大收益
  • 那么 \(i\) 这个物品转移的状态只有 $a[i].in $ 和 \(a[i].out\) 这个范围内,因为只有在这个范围内才保证是其上边的物品,在它前边但不相交的物品,是在它进入之前就已经进去又出去了,所以不能用它转移
  • 还设 \(h[u][j]\) 表示 在 \(i\) 这个物品在背包的时间内, 到 \(u\) 这个时间点,最大限重不超过 \(j\)\(f\) 的前缀和最大
  • 这个数组的意义
  • enter image description here
  • 为了算上A之前的物品

代码AC

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int n,S,ans;
int f[505][1002],h[1005][1002];
struct node{
	int in,out,s,w,v;
}a[505];
bool cmp(node a,node b){
	if(a.out ==b.out ) return a.in >b.in ;
	return a.out <b.out ;
}
int main()
{
	scanf("%d%d",&n,&S);
	for(int i=1;i<=n;i++) scanf("%d%d%d%d%d",&a[i].in,&a[i].out,&a[i].w,&a[i].s,&a[i].v);
	a[n+1].out =n*2;
	a[n+1].s =S;
	n++; 
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++){
		int up=min(a[i].s ,S-a[i].w),j=1;
		memset(h,0,sizeof(h));
		while(a[j].out <=a[i].in &&j<n) j++;//排除这个物品之外的物品 
		for(int k=a[i].in+1;k<=a[i].out;k++){
			memcpy(h[k],h[k-1],sizeof(h[k]));
			for(;a[j].out ==k&&j<i;j++){
				int x=a[j].in ;
				if(x<a[i].in ) continue;
				for(int u=0;u<=up;u++) h[k][u]=max(h[k][u],h[x][u]+f[j][u]);
			}
		}			
		for(int t=0;t<=up;t++) f[i][t+a[i].w]=h[a[i].out][t]+a[i].v ;
		for(int u=1;u<=S;u++) f[i][u]=max(f[i][u],f[i][u-1]); 
	}
	printf("%d",f[n][S]);
	return 0;
} 

暴力代码20

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,s,ans,nn;
int p[505];
struct node{
	int pin,pout,w,s,v,nowei;
}e[505],ton[505];
bool cmp(node a,node b){
	if(a.pin ==b.pin ) return a.pout >b.pout ;
	return a.pin <b.pin ;
}
void cal(int x){
	int jus=0,res=0;
	for(int i=0;i<=2*n;i++){
		while(ton[res].pout==i&&res>0) {
			jus=jus+ton[res].v;
			ans=max(ans,jus);
			res--;
		}
		if(ton[res].pout <i&&res) return ; 
		for(int j=0;j<x;j++){
			if(e[p[j]].pin!=i) continue;
		  	for(int k=1;k<=res;k++){
		  		ton[k].nowei+=e[p[j]].w ;
		  		if(ton[k].nowei >ton[k].s ||ton[k].nowei >s) return ;
			}
		  	ton[++res]=e[p[j]];
//		  	for(int i=1;i<=res;i++) printf("%d ",ton[i].nowei );
		  	
		}
//		cout<<endl;
	}
	ans=max(ans,jus);
}
void dfs(int now,int cnt){
	if(now==n){
		cal(cnt);
		return ;
	}
	for(int i=now+1;i<=n;i++){
		p[cnt]=i;
		dfs(i,cnt+1);
		p[cnt]=0;
	} 
}
int main()
{
	scanf("%d%d",&n,&s);
	for(int i=1;i<=n;i++) {
		scanf("%d%d%d%d%d",&e[i].pin,&e[i].pout,&e[i].w,&e[i].s,&e[i].v);
		nn=max(nn,e[i].pout);
	}
			
	sort(e+1,e+n+1,cmp);
//	for(int i=1;i<=n;i++) printf("%d %d\n",e[i].pin ,e[i].pout );
//	cout<<endl;
	dfs(0,0);
	printf("%d",ans);
	return 0;
}

D

  • \(n\) 个盒子看成 \(n\) 个二进制数, 二进制的位数是 \(m\)\(0,1\) 代表有没有
  • 我们最终的答案是 \(2^m-1\) 这种状态的方案数
  • 考虑递推
  • 我们设 \(g[i]\) 表示 一种二进制状态转成十进制数 \(i\) 的个数,也就是统计这\(n\) 个状态中每种状态的个数
  • \(G[i]\) 表示 \(i\) 这种状态及其子集的 个数
  • \(f[i]\) 表示并集为 \(i\) 及其子集的方案数

\(f[i]=2^{G[i]}\)

  • 相当于这 \(G[i]\) 个状态为 \(i\) 的所有子集(包括全集,选还是不选,也就是拼全是1的过程,当然除了全集其他状态都不合法
  • 所以,我们只要去掉 \(f[i]\) 中所有子集,只留下全集就是答案了
  • 我们求 \(f\) 时是求 \(g\) 的所有子集,现在我们要去掉 \(f\) 的所有子集,只要求出来减一下就好了
  • 关键是如何求出一个集合的子集个数
  • 设集合 \(A=111\) 它的子集有 {001},{010},{100},{011},{101},{110},
  • 如果这一位是1 我们可以改成0 其他位不变就是它的子集,它的子集的二进制一定比它小,因为我们是递推比他小的答案已经算过了,所以直接算就行。

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#define ll long long 
using namespace std;
const ll MOD=1e9+7;
int n,m;
ll p[10000005];
ll g[10000006],f[10000005];
int main()
{
	scanf("%d%d",&n,&m);
	int up=(1<<m)-1;
	for(int i=1;i<=n;i++){
		int num,x,res=0;
		scanf("%d",&num);
		for(int j=1;j<=num;j++){
			scanf("%d",&x);
			x--;
			res|=(1<<x);
		} 
		g[res]++;
	}
	for(int i=0;i<=m;i++)
	  for(int j=0;j<=up;j++)
	    if(j&(1<<i)) g[j]+=g[j^(1<<i)];
	p[0]=1;
	for(int i=1;i<=n;i++) p[i]=(p[i-1]<<1)%MOD;
	for(int i=0;i<=up;i++) f[i]=p[g[i]];
	for(int i=0;i<=m;i++)
	  for(int j=0;j<=up;j++)
	    if(j&(1<<i)) f[j]=(f[j]-f[j^(1<<i)]+MOD)%MOD;
	printf("%lld",f[up]);
	return 0;
}
posted @ 2019-09-11 19:32  _Vimin  阅读(191)  评论(0编辑  收藏  举报