我初学除背包以外的dp,先从线性dp开始
【红牌】
题目描述
某地临时居民想获得长期居住权就必须申请拿到红牌。获得红牌的过程是相当复杂 ,一共包括NN个步骤。每一步骤都由政府的某个工作人员负责检查你所提交的材料是否符合条件。为了加快进程,每一步政府都派了MM个工作人员来检查材料。不幸的是,并不是每一个工作人员效率都很高。尽管如此,为了体现“公开政府”的政策,政府部门把每一个工作人员的处理一个申请所花天数都对外界公开。
为了防止所有申请人都到效率高的工作人员去申请。这M \times NM×N个工作人员被分成MM个小组。每一组在每一步都有一个工作人员。申请人可以选择任意一个小组也可以更换小组。但是更换小组是很严格的,一定要相邻两个步骤之间来更换,而不能在某一步骤已经开始但还没结束的时候提出更换,并且也只能从原来的小组I更换到小组I+1I+1,当然从小组MM可以更换到小组11。对更换小组的次数没有限制。
例如:下面是33个小组,每个小组44个步骤工作天数:
小组11 : 2, 6 ,1 ,82,6,1,8
小组22 : 3,6, 2, 63,6,2,6
小组33 : 4, 2 ,3 ,64,2,3,6
例子中,可以选择小组11来完成整个过程一共花了2+6+1+8=172+6+1+8=17天,也可以从小组22开始第一步,然后第二步更换到小组3,第三步到小组11,第四步再到小组22,这样一共花了3+2+1+6=123+2+1+6=12天。你可以发现没有比这样效率更高的选择。
你的任务是求出完成申请所花最少天数。
输入格式
第一行是两个正整数NN和MM,表示步数和小组数。接下来有MM行,每行NN个非负整数,第i+1(1 \le i \le M)i+1(1≤i≤M)行的第j个数表示小组ii完成第j步所花的天数,天数都不超过10000001000000。
输出格式
一个正整数,为完成所有步所需最少天数。
输入输出样例
输入 #1复制
4 3 2 6 1 8 3 6 2 6 4 2 3 6
输出 #1复制
12
说明/提示
【数据规模与约定】
对于100\%100%的数据,有N ≤ 2000, M ≤ 2000N≤2000,M≤2000。
dp[i][j] i维度我在第i次选择
j维度我在第j行
if(j==m)
{dp[i][j]=max(dp[i-1][j]+a[i][j],dp[i-1][j]+a[i][1]}
else
{dp[i][j]=max(dp[i-1][j]+a[i][j],dp[i-1][j]+a[i][j+1]}
遍历dp[n][j]找最大值
这是我一开始想的,后来算出来是2+2+1+6=11;
根据样例,应该是取第三行,然后和第一行一直取小
我心想,难度是第一个数只能是自己?
后来搞出来是13
然后我觉得不对,我读了一遍题目,它的意思是1可以到2,2之后可以到3,那就跟我的状态转移不一样了。
后来看了一下题解,状态转移应该是
dp[i][j] i维度我在第i次选择
j维度我在第j行
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1])+a[j][i];
遍历dp[n][j]找最大值
另外j==1的时候一部分是从上一维j==m转移过来的,特判一下就行。
自己空想的第二题动规还是没有实现。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2005;
ll dp[maxn][maxn];
int a[maxn][maxn];
int n,m;
ll ans=1e18;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(j==1)
{
dp[i][j]=min(dp[i-1][j],dp[i-1][m])+a[j][i];
}
else
{
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1])+a[j][i];
}
}
}
for(int i=1;i<=m;i++)
{
ans=min(ans,dp[n][i]);
}
cout<<ans;
return 0;
}