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;
}