zoukankan      html  css  js  c++  java
  • 暑 假 队 测 Round #4

    (25+25+50=100pts).

    T1:Folding
    T2:数字串
    T3:炸毁城市

    T1:区间dp

    非常经典的区间dp题。

    • 如果不能压缩:(f[l][r]=f[l][k]+f[k+1][r])
    • 如果能压缩:(f[l][r]=cnt+2+f[l][k]) (cnt指压缩后的长度)

    求解过程中需要判断两个字符串是否相等,hash可以O(1)查询。
    但这题数据范围较小,暴力比较即可。

    Code:

    #include<bits/stdc++.h>
    #define ll long long
    #define inf 30
    using namespace std;
    const ll N=150;
    const ll mod=19260817;
    const ll b=161;
    ll f[N][N],sum[N],p[N];
    char st[N];
    int check(int l,int m,int r){
    	int len2=r-l+1,len1=m-l+1;
    	if(len2%len1)return inf;
    	int x=len2/len1;
    	for(int i=1;i<=x;i++){
    		int tt=l+(i-1)*len1;
    		for (int j=0;j<len1;j++)
    			if(st[l+j-1]!=st[tt+j-1])return inf;
    	}
    	return x;
    }
    int main(){
    	p[0]=1;
    	scanf("%s",st);
    	ll n=strlen(st);
    //	for(ll i=1;i<n;i++){
    //		sum[i]=(sum[i-1]*b%mod+(st[i]-'A'+1)%mod)%mod;
    //		p[i]=p[i-1]*b%mod;
    //	}
    	memset(f,0x3f,sizeof(f));
    	for(ll i=1;i<=n;i++)f[i][i]=1;
    	for(ll len=2;len<=n;len++)
    		for(ll l=1;l<=n-len+1;l++){
    			ll r=l+len-1;
    			f[l][r]=min(f[l][r],len);
    			for(ll k=l;k<r;k++){
    				f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
    				ll bk=check(l,k,r);
    				if(bk==inf)continue;
    				ll num=bk,cnt=0;
    				while(num>0)num/=10,cnt++;
    				f[l][r]=min(f[l][r],cnt+2+f[l][k]);
    			}	
    		}
    	printf("%lld",f[1][n]);
    	return 0;
    }
    

    T2:我也不知道怎么做

    听这题时在划水。。代码也没写出来。
    这里口胡一下部分分做法吧。

    (10pts): 怎么暴力怎么来。
    (25) ~ (40pts):依次拆分每个数,前缀和统计,O(1)回答查询,注意细节能够拿到40甚至更多,像我写的这么烂只有25。
    (70pts):40分做法+压位。
    (100pts):
    这题正解细节较多的,稍不小心就会沦为和暴力老哥同分。
    n位二进制所有1的个数是可以直接求的。
    (2^{n-1}+(n-1) imes2^{n-2})
    可以用二分快速求出求得长度。
    我们知道第一位一定是1 如(100,1000,10000)
    我们统计1,然后删除前导0,这样就又回到原来的问题,所以可以用递归求解。

    T3:tree dp

    第一个回答是个很简单的treedp。
    战略游戏一模一样。
    不懂的可以去看下qzwer大佬的Blog
    而第二问难度就加强了很多。
    (f[i][1])为炸第i个结点对整棵树来说最少需要炸多少个结点,(f[i][0])则不炸.
    当一个结点在任何最优情况下都不可能被炸掉时,显然:

    • (f[i][1]>f[i][0])

    一个很直观的想法就是以每个结点为根,做一次与第一问同样的treedp。
    每次进行判断,如果对结果有贡献,就做上标记,最后输出没有标记的节点。
    时间复杂度(O(n^2)),只能拿到50分,机房有大佬通过玄学优化艹到了80分。
    正解是换根+二次扫描。讲解会比较啰嗦。直接上代码le.

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;
    int n,tot,f[N][2],g[N][2],r[N][2];
    int pre[N],now[N],to[N],sm[N],sg[N];
    void add(int x,int y){
    	pre[++tot]=now[x];
    	to[tot]=y;now[x]=tot;
    }
    void dfs(int u,int fa){
    	f[u][1]=1,f[u][0]=0;
    	for(int i=now[u];i;i=pre[i]){
    		int v=to[i];
    		if(v==fa)continue;
    		dfs(v,u);
    		f[u][1]+=min(f[v][1],f[v][0]);
    		f[u][0]+=f[v][1];
    	}
    }
    void dp(int u,int fa){
    	for(int i=now[u];i;i=pre[i]){
    		int v=to[i];
    		if(v==fa)continue;
    		g[v][1]=r[u][1]-min(f[v][1],f[v][0]);
    		g[v][0]=r[u][0]-f[v][1];
    		r[v][0]=sm[v]+g[v][1];
    		r[v][1]=sg[v]+min(g[v][0],g[v][1])+1;
    		dfs(v,u);
    	}
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1,u,v;i<n;i++){
    		scanf("%d%d",&u,&v);
    		add(u,v);add(v,u);
    	}	
    	dfs(1,0);
    	int ans=min(f[1][0],f[1][1]);
    	printf("%d
    ",ans);
    	r[1][1]=f[1][1],r[1][0]=f[1][0];
    	for(int i=1;i<=n;i++)
    		for(int j=now[i];j;j=pre[j])
    			sm[i]+=f[to[j]][1],sg[i]+=min(f[to[j]][1],f[to[j]][0]);
    	dp(1,0);
    	for(int i=1;i<=n;i++)
    		if(r[i][0]<=r[i][1])printf("%d ",i);
    	return 0;
    }
    
  • 相关阅读:
    位运算的应用
    MySql的自增主键以及TEXT数据类型使用
    MaxDos启动盘拆解
    QT预备式(包含MySql配置)未完成……
    关于Services.exe开机CPU内存使用暴增解决方案
    Windows Upnp 服务补丁——UpnpFix V1.0
    Memory Ordering
    "FoxitReaderOCX.ocx failed to load" 问题解决方案
    LameDropXPd V2.0 (L.A.M.E 3.97) 完美汉化版
    编译QT的MySql驱动问题及解决方案
  • 原文地址:https://www.cnblogs.com/Xxhdjr/p/13508994.html
Copyright © 2011-2022 走看看