dp相关题目(背包,最短Hamilton路径)

1、CodeForces(Yet Another Subarray Problem)

题目链接:http://codeforces.com/contest/1197/problem/D
题意:给一个长度为n和m,k的数组,求最大值。

/*
考虑dp的做法,dp[i][j]代表以第i个数为右端点,长度减一求余m的值为j时的最大值。
转移方程:dp[i][j]=dp[i-1][j-1]+a[i](j>0)
     dp[i][j]=max(dp[i-1][m-1]+a[i]-k,a[i]-k)(j==0)
*/

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;;
const int maxn=3e5+5;
ll dp[maxn][12];
ll a[maxn];

int main(){
    ll n,m,k;
    cin>>n>>m>>k;
    for(int  i=1;i<=n;i++)
    	scanf("%lld",&a[i]);
    for(int i=0;i<=n;i++)
    	for(int j=0;j<=10;j++)
    		dp[i][j]=-1e18;	//保证不会和输入的数冲突
	dp[0][m-1]=0;
	ll ans=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<m;j++){
			if(j==0)
				dp[i][j]=max(dp[i-1][m-1]+a[i]-k,a[i]-k);
			else
				dp[i][j]=dp[i-1][j-1]+a[i];
			ans=max(ans,dp[i][j]);
			}
		}
		printf("%lld\n",ans);	 
    return 0;
}

2、HDU 1114(完全背包)

#include<iostream>
#include<iomanip>
#include<string.h>
#include<algorithm> 
using namespace std;

int dp[10010]; 
int main(){
	int te,e,f,n,cost,value,v;
	cin>>te;
	while(te--){
		cin>>e>>f;
		v=f-e;
		cin>>n;
		memset(dp,-1,sizeof(dp));//初始化,-1表示状态未到达 
		dp[0]=0;
		for(int i=0;i<n;i++){
			cin>>value>>cost;
			for(int i=0;i+cost<=v;i++){
				if(dp[i]==-1)continue;//状态未达到,不能进行转移 
				else if(dp[i+cost]==-1)//如果第一次到达该状态,直接赋值 
					dp[i+cost]=dp[i]+value;
				else
					dp[i+cost]=min(dp[i+cost],dp[i]+value);
			}
			}
			if(dp[v]==-1)//dp[v]为-1说明所有银币任意组合都不能得到v 
				cout<<"This is impossible."<<endl;
			else
				cout<<"The minimum amount of money in the piggy-bank is "<<dp[v]<<"."<<endl;		
	}
		return 0;
} 

3、最短Hamilton路径(二进制状压dp)

题目来源点链接:牛客

二进制状态压缩中常用位操作
  • 取状态为sta,位置为 i 的数字:sta&(1<<i)
  • 把第 i 位设置为1:sta=sta|(1<<i)
  • 把第 i 位设置为0:sta=sta&(~(1<<i) )
  • 把第 i 位取反:sta=sta^(1<<i)
  • 取出sta的最后一个1:(sta&-sta),运用补码表示的相关知识

用二进制上的数代表一个点的状态,取(1)或不取(0)。题目让求从点1到n的最短汉密顿路径,即经过每个点一次,这时的状态用二进制表示就是 (1<<n)-1 (n个1)。用dp[i][j]表示在状态 i 下,从1到 j 的最短汉密顿路径。

dp[i][j]可由上一个状态(上一状态就是把 j从当前状态中去掉)dp[i^(1<<(j-1))][k]得到,其中保证k是中存在的点,即 (i>>k)&1。表示 i 的第 k 位是1,即经过点 k。 注意是 i>>k 不是 i<<k

则状态转移方程为:dp[i][j]=min{dp[i^(1<<j)][k]+Map[k][j]}(k=1~n); 其含义就是枚举到达点j之前的前一个点k,取其最短;

参考链接:https://blog.youkuaiyun.com/qq_41021816/article/details/81808246

/*用dp[i][j]表示在状态 i 下,从1到 j 的最短汉密顿路径
dp[i][j]可由上一个状态(上一状态就是把 j从当前状态中去掉)dp[i^(1<<(j-1))][k]得到,其中保证k是中存在的点,即 (i>>k)&1。表示 i 的第 k 位是1,即经过点 k。 
dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+gra[k][j]);含义就是枚举到达点j之前的前一个点k,取其最短
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
 
int a[21][21],dp[1<<21][21];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
    	for(int j=0;j<n;j++)
    		scanf("%d",&a[i][j]);
	}
	memset(dp,0x3f,sizeof(dp));
	dp[1][0]=0;
	for(int i=1;i<=(1<<n)-1;i++){
		for(int j=0;j<n;j++){
			if((i>>j)&1){//如果i的第j位是1,即经过点j 
				for(int k=0;k<n;k++){
					if((i>>k)&1){//如果i的第k位是1,经过k 
						dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+a[k][j]);
					}
				}
			}
		}
	}
	 printf("%d\n",dp[(1<<n)-1][n-1]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值