暑假集训D2 2023.7.25 补题

发布时间 2023-07-25 20:21:22作者: LZH_sdust

D.P1796 汤姆斯的天堂梦

这道题目非常ex,赛时死活调不出来,思路是对的,容易发现是一个DAG,所以直接DP就好,虽然后面看题解AC了,发现是重边的问题。但还是来记录一下这道ex的题目,警醒一下自己切记注意重边!!
如下两份代码,一份爆0,一份AC

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define endl '\n'
#define int long long
using namespace std;

const int N = 1e5+10;
int dp[110][110];
int n,k[110];
int g[110][110][110];

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	memset(dp,0x3f,sizeof dp);
	memset(g,0x3f,sizeof g);
	
	k[0] =1;
	dp[0][1]  =0;
	for(int i =1;i<=n;i++)
	{
		cin>>k[i];
		for(int j =1;j<=k[i];j++)
		{
			int x,cost;
			while(cin>>x&&x)
			{
				cin>>cost;
				g[i-1][x][j] = cost;
				
			}
		}
	}
	
	
	for(int dep= 1;dep<=n;dep++)
	{
		for(int i =1;i<=k[dep];i++)
		{
			for(int j =1;j<=k[dep-1];j++)
			{
				dp[dep][i] = min(dp[dep][i],dp[dep-1][j]+g[dep-1][j][i]);
				//cout<<dep<<":"<<j<<" "<<i<<" "<<dp[dep][i]<<endl;
			}
			
		}
	}
	int res = 2147483647;
	for(int i =1;i<=k[n];i++)
	{
		res = min(res,dp[n][i]);
	}
	cout<<res;
	return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define endl '\n'
#define int long long
using namespace std;

const int N = 1e5+10;
int dp[110][110];
int n,k[110];
int g[110][110][110];

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	memset(dp,0x3f,sizeof dp);
	memset(g,0x3f,sizeof g);
	
	k[0] =1;
	dp[0][1]  =0;
	for(int dep =1;dep<=n;dep++)//星球等级
	{
		cin>>k[dep];
		for(int i =1;i<=k[dep];i++)//终点
		{
			int j,cost;//j为起点,cost为从j到i的花费
			while(cin>>j&&j)
			{
				cin>>cost;
				g[dep-1][j][i] = cost;
				
				dp[dep][i] = min(dp[dep][i],dp[dep-1][j]+g[dep-1][j][i]);
			}
		}
	}
	
	int res = 2147483647;
	for(int i =1;i<=k[n];i++)
	{
		res = min(res,dp[n][i]);
	}
	cout<<res;
	return 0;
}

只需要输入时取g[i-1][x][j] = min(g[i-1][x][j],cost);

L. P2663 越越的组队

看了半天题目毫无头绪.遂看题解

此题为背包变形题目,每个同学只能选1次,跟01背包非常类似.容量上限就是\(sum/2\).01背包一维是记录前i个人,一维记录当前的分数.但是加了一个限制:必须选择恰好一半的同学.
因此加一维记录一下选的人数就好了.
dp[i][j][k]表示前i个人中选j个总分是否能达到k
朴素背包代码如下

for(int i=1;i<=n;i++)
{
	dp[i][0][0] =1;
	for(int j = 1;j<=i;j++)
	{
		for(int k = 0;k<=5000;k++)
		{
			dp[i][j][k] = dp[i-1][j][k];
			if(!dp[i][j][k]&&k>=a[i])
			{
				dp[i][j][k] = dp[i-1][j-1][k-a[i]];
			}
		}
	}
}

可以对第一层进行压缩.不再赘述
完整代码

点击查看代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define endl '\n'
using namespace std;
const int N = 1e5+10;
int dp[110][10010];
int n;
int a[N];
int sum = 0;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}
	dp[0][0] =1;
	for(int i=1;i<=n;i++)
	{
		for(int j = i;j>=1;j--)
		{
			for(int k = 5000;k>=0;k--)
			{
				//dp[i][j][k] = dp[i-1][j][k];
				if(!dp[j][k]&&k>=a[i])
				{
					dp[j][k] = dp[j-1][k-a[i]];
				}
			}
		}
	}
	int res = 0;
	for(int i =0;i<=sum/2;i++)
	{
		if(dp[n/2][i])
		{
			res = i;
		}
	}
	cout<<res;
	return 0;
}