机房模拟20180814

日常................

\

LIS裸题............真的不需要题解吧......

设定一个数组low[i]表示长度为i的LIS的最小的结尾,我们可以发现low内的数组一定是单增的,所以我们可以在a【i】大于末项的时候更新,然后将每一个不大于末项的a[i]用lower_bound二分找到其在low中的位置然后更新

最后输出low数组的长度就好了

#include<cstdio>
#include<algorithm>
const int MAXN=1e5+5;
const int INF=0x7fffffff;
int low[MAXN];
int a[MAXN];
int n;
int ans;
int main()
{
	//std::freopen("lis.in","r",stdin);
	//std::freopen("lis.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]); 
        low[i]=INF;
    }
    low[1]=a[1]; 
    ans=1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]>low[ans])
        {
        	low[++ans]=a[i];
        }
        else
        {
        	low[std::lower_bound(low+1,low+1+ans,a[i])-low]=a[i];
        }
    }
    printf("%d\n",ans);
    return 0;
}

 T2

树形dp,然而我用状压的假算法苟到了40分............

我们定义dp[u][i]为在u为根的子树当中,选取i个点构成联通块需要的最小割边数量

转移方程简单的要死........自己都不知道考试的时候为什么想不到

dp[u][i]=std::min(dp[u][i]+1,min(dp[u][i-j]+dp[v][j]))

全部枚举直接dp就完成了

#include<cstdio>
#include<cstring>
#include<algorithm>
const int MAXN=150+5;
const int INF=1e9+7;
class Edge
{
	public:
		int nxt;
		int to;
}edge[MAXN<<1];
int head[MAXN]; 
int num;
void add(int from,int to)
{
	edge[++num].nxt=head[from];
	edge[num].to=to;
	head[from]=num;
}
int n,k;
int siz[MAXN];
int dp[MAXN][MAXN];
int ans=INF;
void dfs(int u,int f)
{
	std::memset(dp[u],63,sizeof(dp[u]) );
	siz[u]=1;
	dp[u][1]=0;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==f)
		{
			continue;
		}
		dfs(v,u);
		siz[u]+=siz[v];
		for(int i=k;i>=1;i--) 
		{
			int tmp=dp[u][i]+1;
            for(int j=1;j<i;j++)
            {
            	tmp=std::min(tmp,dp[v][i-j]+dp[u][j]);//dp[u][i]=std::min(dp[u][i]+1,min(dp[u][i-j]+dp[v][j]))
            }
            dp[u][i] = tmp;
        }
	}
}
int main()
{
	//std::freopen("isolate.in","r",stdin);
	//std::freopen("isolate.out","w",stdout);
	std::scanf("%d%d",&n,&k);
	if(n==k)
	{
		std::printf("0\n");
	}
	for(int i=1;i<=n-1;i++)
	{
		int u,v;
		std::scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dfs(1,0);
	int ans=dp[1][k];
	for(int i=2;i<=n;i++)
	{
		ans=std::min(ans,dp[i][k]+1);
	}
	std::printf("%d\n",ans);
	return 0;
}
/*
11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11

5 3
1 2
2 3
2 4
2 5
*/

T3

轮廓线裸题,全班AC率最高的题目.......

定义dp[i][s]为状态为s,取到第i行的方案数,然后按照正常的轮廓线更新就好了

具体轮廓线的套路比较复杂,在这里就不讲了

详情可以参见这篇博客

https://blog.csdn.net/cyendra/article/details/38171319

代码实现非常简单

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
const int MAXN=15;
LL dp[2][1<<MAXN];
int r,c; 
int now;
void solve(int s1,int s2)
{
    if(s2&(1<<c))
	{
		dp[now][s2^(1<<c)]+=dp[now^1][s1];
	}
}
int main()
{
	//std::freopen("domino.in","r",stdin);
	//std::freopen("domino.out","w",stdout);
	std::scanf("%d%d",&r,&c);
    if(c>r)
	std::swap(r,c);
    memset(dp,0,sizeof(dp));
    now=0;
    dp[now][(1<<c)-1]=1;
    for(int i=0;i<r;i++)
    {
        for(int j=0;j<c;j++)
        {
            now^=1;
            memset(dp[now],0,sizeof(dp[now]));
            for(int s=0;s<(1<<c);s++)
            {
                solve(s,s<<1);
                if(i&&!(s&(1<<(c-1))))
				{
					solve(s,(s<<1)^(1<<c)^1);
				} 
                if(j&&(!(s&1)))
				{
					solve(s,(s<<1)^3);
				} 
            }
        }
    }
    printf("%I64d\n",dp[now][(1<<c)-1]);
    return 0;
}

T4

 

 背包裸题

设dp[k][t]表示现在选取了价值总和为k的集合中是否能包含t

而新添加的元素一定对k产生影响,一定会加上a[i]

而对于t可能影响可能不影响

所以直接转移就好

dp[j+a[i]][t]|=dp[j][t];
dp[j+a[i]][t+a[i]]|=dp[j][t];

#include<cstdio>
#include<algorithm>
const int MAXN=505;
bool dp[MAXN][MAXN];
int a[MAXN];
int main()
{
	//std::freopen("coin.in","r",stdin);
	//std::freopen("coin.out","w",stdout);
	int n,k;
	std::scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		std::scanf("%d",a+i);
	}
	dp[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=k-a[i];j>=0;j--)
		{
			for(int t=j;t>=0;t--)
			{
				if(dp[j][t])
				{
					dp[j+a[i]][t]|=dp[j][t];
					dp[j+a[i]][t+a[i]]|=dp[j][t];
				}
			}
		}
	}
	int cnt=0;
	for(int i=0;i<=k;i++)
	{
		if(dp[k][i])
		{
			cnt++;
		}
	}
	std::printf("%d\n",cnt);
	for(int i=0;i<=k;i++)
	{
		if(dp[k][i])
		{
			std::printf("%d ",i);
		}
	}
	std::printf("\n");
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值