暑假训练DAY12(区间DP与树型DP)
1592:石子归并
提交页面 概要 时间限制: 1秒 内存限制:128 Mb已 提交: 913已 解决: 435
描述
现在有Ñ堆石子,第我堆有人工智能个石子。现在要把这些石子合并成一堆,每次只能合并相邻两个,每次合并的代价是两堆石子的总石子数。求合并所有石子的最小代价。
输入
第一行包含一个整数T(T <= 50),表示数据组数。
每组数据第一行包含一个整数N(2 <= N <= 100),表示石子的堆数。
第二行包含Ñ个正整数AI(AI <= 100),表示每堆石子的石子数。
产量
每组数据仅一行,表示最小合并代价。
样本输入
2
4
1 2 3 4
五
3 5 2 1 4
样本输出
19
33
暗示
资源
国防科学技术大学第十八届银河之光文化节ACM程序设计竞赛初赛
区间DP的模板题了,i到j的最小值就是从i到k的最小值加上从k+1到j的最小值加上把i到j全部合并的代价也就是中间所有的石子的重量即可:ans=min(ans,dfs(l,k)+dfs(k+1,r)+sum[r]-sum[l-1]);
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
int dp[105][105];
int w[105];
int t;
int sum[105];
const int inf=0x3f3f3f3f;
int dfs(int l,int r)
{
if(dp[l][r]!=-1) return dp[l][r];
//int sum=0;
int ans=inf;
for(int k=l;k<r;k++)
{
ans=min(ans,dfs(l,k)+dfs(k+1,r)+sum[r]-sum[l-1]);
}
return dp[l][r]=ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
int n;
memset(dp,-1,sizeof(dp));
memset(w,0,sizeof(w));
memset(sum,0,sizeof(sum));
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&w[i]);
if(i!=0)dp[i-1][i]=w[i]+w[i-1];
dp[i][i]=0;
if(!i) sum[0]=w[0];
else
{
sum[i]=sum[i-1]+w[i];
}
}
int ans=dfs(0,n-1);
cout<<ans<<endl;
}
return 0;
}
周年纪念派对
时间限制:2000/1000 MS(Java / Others)内存限制:65536/32768 K(Java / Others)
总提交内容:16665接受的提交内容:6337
问题描述
将有一个庆祝乌拉尔国立大学成立80周年的派对。大学有员工的等级结构。这意味着主管关系形成了一个植根于VE Tretyakov校长的树。为了使每个人都对派对感到好笑,校长不希望雇员和他或她的直接主管在场。人事办公室已评估每位员工的欢乐程度,因此每个人都有一些(评级)附加给他或她。您的任务是列出具有最大可能客人欢乐等级的客人名单。
输入
员工编号从1到N.第一行输入包含编号N. 1 <= N <= 6 000.后续N行中的每一行都包含相应员工的欢乐评级。Conviviality rating是一个整数,范围从-128到127.然后转到描述主管关系树的T行。树规范的每一行都有以下形式:
LK
这意味着第K名员工是第L名员工的直接主管。输入以
0 0 行结束
产量
输出应包含客人评级的最大总和。
样本输入
7 1 1 1 1 1 1 1 1 3 2 3 6 4 7 4 4 5 3 5 0 0
样本输出
五
资源
最小独立集,树型DP模板题了,详细解释,指路https://blog.csdn.net/baiyifeifei/article/details/81394549
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int size=6005;
int edge[size];
int dp[size][2];
int unroot[size];
int v[size];
vector<int> son[size];
int flag[size];
void dfs(int node)
{
flag[node]=1;
for(int i=0;i<son[node].size();i++)
{
dfs(son[node][i]);
int s=son[node][i];
dp[node][0]+=max(dp[s][0],dp[s][1]);
dp[node][1]+=dp[s][0];
}
}
int main()
{
int n;
while(cin>>n)
{
memset(dp,0,sizeof(dp));
memset(unroot,0,sizeof(unroot));
memset(edge,0,sizeof(edge));
memset(flag,0,sizeof(flag));
int i;
for(i=1;i<=n;i++)
{
scanf("%d",&v[i]);//读入每个人的快乐值
son[i].clear();
}
int a,b;
while(~scanf("%d%d",&a,&b)&&a)
{
son[b].push_back(a);//a的父节点是b
unroot[a]=1;//假如他有过作子节点的经历,则一定不是根节点
}
int root;
for(i=1;i<=n;i++)
{
if(!unroot[i])
{
root=i;
}
dp[i][1]=v[i];
dp[i][0]=0;
}
dfs(root);
int maxn=max(dp[root][0],dp[root][1]);
cout<<maxn<<endl;
}
return 0;
}
乘法拼图POJ - 1651
时间限制: 1000MS | 内存限制: 65536K | |
提交总数: 13138 | 接受: 8051 |
描述
乘法拼图使用一排卡片,每个卡片包含一个正整数。在移动过程中,玩家从一行中取出一张牌,并将得分数等于卡上的数字和左侧及右侧卡上的数字的乘积。不允许取出行中的第一张和最后一张牌。最后一步之后,行中只剩下两张牌。
目标是按照最小化得分点总数的顺序取卡。
例如,如果行中的牌包含数字10 1 50 20 5,则牌手可能会获得1,然后是20和50的牌,得分
10 * 1 * 50 + 50 * 20 * 5 + 10 * 50 * 5 = 500 + 5000 + 2500 = 8000
如果他以相反的顺序拿出牌,即50,然后是20,那么1,得分就是
1 * 50 * 20 + 1 * 20 * 5 + 10 * 1 * 5 = 1000 + 100 + 50 = 1150。
输入
输入的第一行包含卡的数量N(3 <= N <= 100)。第二行包含N个整数,范围从1到100,用空格分隔。
产量
输出必须包含一个整数 - 最小分数。
样本输入
6 10 1 50 50 20 5
样本输出
3650
区间DP
中间选出一个值将其分为前后两部分,前面的最小值,加后半部分的的最小值,加上该值乘上整个区段起始的值和末尾的值
ans=min(ans,dfs(l,k)+dfs(k+1,r)+arr[k]*arr[l-1]*arr[r]);
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int inf=0x3f3f3f3f;
int n,arr[105];
int dp[105][105];
int dfs(int l,int r)
{
if(dp[l][r]!=0) return dp[l][r];
if(l==r) return dp[l][r];
int ans=inf;
for(int k=l;k<r;k++)
{
ans=min(ans,dfs(l,k)+dfs(k+1,r)+arr[k]*arr[l-1]*arr[r]);
}
return dp[l][r]=ans;
}
int main()
{
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
memset(arr,0,sizeof(arr));
for(int i=0;i<n;i++)
{
scanf("%d",&arr[i]);
//dp[i][i]=0;
//if(i!=0) dp[i-1][i]=0;
if(i!=0&&i!=n-1) dp[i][i]=arr[i]*arr[i+1]*arr[i-1];
//if(i!=0&&i-1!=0) dp[i-2][i]=arr[i]*arr[i-1]*arr[i-2];
}
int ans=dfs(1,n-1);
cout<<ans<<endl;
}
return 0;
}
B. Zuma
time limit per test
2 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output
Genos recently installed the game Zuma on his phone. In Zuma there exists a line of n gemstones, the i-th of which has color ci. The goal of the game is to destroy all the gemstones in the line as quickly as possible.
In one second, Genos is able to choose exactly one continuous substring of colored gemstones that is a palindrome and remove it from the line. After the substring is removed, the remaining gemstones shift to form a solid line again. What is the minimum number of seconds needed to destroy the entire line?
Let us remind, that the string (or substring) is called palindrome, if it reads same backwards or forward. In our case this means the color of the first gemstone is equal to the color of the last one, the color of the second gemstone is equal to the color of the next to last and so on.
Input
The first line of input contains a single integer n (1 ≤ n ≤ 500) — the number of gemstones.
The second line contains n space-separated integers, the i-th of which is ci (1 ≤ ci ≤ n) — the color of the i-th gemstone in a line.
Output
Print a single integer — the minimum number of seconds needed to destroy the entire line.
Examples
input
Copy
3 1 2 1
output
Copy
1
input
Copy
3 1 2 3
output
Copy
3
input
Copy
7 1 4 4 2 3 2 1
output
Copy
2
Note
In the first sample, Genos can destroy the entire line in one second.
In the second sample, Genos can only destroy one gemstone at a time, so destroying three gemstones takes three seconds.
In the third sample, to achieve the optimal time of two seconds, destroy palindrome 4 4 first and then destroy palindrome 1 2 3 2 1
祖玛回文,去年做过一次了。。今年还是不会。。菜的真实
消去一段时,如果其中有两个不相邻的数字相等的话他们两个就可以免费删除(他们最终总是可以附带在中间某个串或者字符上删除)如果相邻则算作一次
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
int dp[505][505];
int arr[505];
int dfs(int l,int r)
{
if(dp[l][r]!=-1) return dp[l][r];
if(l==r) return 1;
if(l>r) return 0;
dp[l][r]=1+dfs(l+1,r);
for(int i=l+2;i<=r;i++)
{
if(arr[l]==arr[i])
{
dp[l][r]=min(dp[l][r],dfs(l+1,i-1)+dfs(i+1,r));
}
}
if(arr[l]==arr[l+1]) dp[l][r]=min(dp[l][r],1+dfs(l+2,r));
return dp[l][r];
}
int main()
{
int n;
while(~scanf("%d",&n))
{
int i;
memset(arr,0,sizeof(arr));
memset(dp,-1,sizeof(dp));
for(i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
}
cout<<dfs(1,n)<<endl;
}
return 0;
}
The more, The Better
Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 9632 Accepted Submission(s): 5593
Problem Description
ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗?
Input
每个测试实例首先包括2个整数,N,M.(1 <= M <= N <= 200);在接下来的N行里,每行包括2个整数,a,b. 在第 i 行,a 代表要攻克第 i 个城堡必须先攻克第 a 个城堡,如果 a = 0 则代表可以直接攻克第 i 个城堡。b 代表第 i 个城堡的宝物数量, b >= 0。当N = 0, M = 0输入结束。
Output
对于每个测试实例,输出一个整数,代表ACboy攻克M个城堡所获得的最多宝物的数量。
Sample Input
3 2 0 1 0 2 0 3 7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2 0 0
Sample Output
5 13
Author
8600
树型背包,本题为了处理上的方便选择了左儿子右兄弟的做法。如果每次推导时分成选当前节点和不选当前节点的做法,如果不选改节点则只能选择该节点的右枝,否则则左右都可以选:
dp[point][lost]=max(dp[point][lost],dfs(tree[point][1],lost));这是只能选右枝的兄弟的情况(对于一个tree节点,1为右节点0为左节点)
dp[point][lost]=max(dp[point][lost],dfs(tree[point][0],k)+v[point]+dfs(tree[point][1],lost-1-k));这是可以选左枝的情况(lost为当前的剩余空间,k为分配给左树的空间)
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
vector<int> son[205];
int v[205];
int tree[205][10];
int dp[205][205];
int n,m;
int dfs(int point,int lost)
{
if(lost==0) return 0;
if(dp[point][lost]!=0)return dp[point][lost];
if(tree[point][1]!=0) dp[point][lost]=max(dp[point][lost],dfs(tree[point][1],lost));
if(point==0) return 0;
for(int k=0;k<lost;k++)
{
dp[point][lost]=max(dp[point][lost],dfs(tree[point][0],k)+v[point]+dfs(tree[point][1],lost-1-k));
}
return dp[point][lost];
}
int main()
{
while(~scanf("%d%d",&n,&m)&&n)
{
for(int i=0;i<=n;i++) son[i].clear();
memset(v,0,sizeof(v));
memset(dp,0,sizeof(dp));
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;i++)
{
int temp;
scanf("%d%d",&temp,&v[i]);
son[temp].push_back(i);
}
for(int i=0;i<=n;i++)
{
int l=son[i].size();
for(int j=0;j<l;j++)
{
if(j==0)
{
tree[i][0]=son[i][0];//左子树是儿子
}
else
{
tree[son[i][j-1]][1]=son[i][j];//右子树是兄弟
}
}
}
int ans=dfs(son[0][0],m);
cout<<ans<<endl;
}
return 0;
}
Running
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 7774 | Accepted: 2903 |
Description
The cows are trying to become better athletes, so Bessie is running on a track for exactly N (1 ≤ N ≤ 10,000) minutes. During each minute, she can choose to either run or rest for the whole minute.
The ultimate distance Bessie runs, though, depends on her 'exhaustion factor', which starts at 0. When she chooses to run in minute i, she will run exactly a distance of Di (1 ≤ Di ≤ 1,000) and her exhaustion factor will increase by 1 -- but must never be allowed to exceed M (1 ≤ M ≤ 500). If she chooses to rest, her exhaustion factor will decrease by 1 for each minute she rests. She cannot commence running again until her exhaustion factor reaches 0. At that point, she can choose to run or rest.
At the end of the N minute workout, Bessie's exaustion factor must be exactly 0, or she will not have enough energy left for the rest of the day.
Find the maximal distance Bessie can run.
Input
* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Line i+1 contains the single integer: Di
Output
* Line 1: A single integer representing the largest distance Bessie can run while satisfying the conditions.
Sample Input
5 2 5 3 4 2 10
Sample Output
9
这题就。。简单DP吧。。需要注意的是,她一旦开始休息就必须把疲劳值休息完才行。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
int dp[10001][500];
int d[10005];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
memset(dp,0,sizeof(dp));
memset(d,0,sizeof(d));
int i;
for(i=1;i<=n;i++)
{
scanf("%d",&d[i]);
}
//dp[1][1]=d[1];
for(i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i==n&&j!=0) break;
dp[i][j]=dp[i-1][j-1]+d[i];
}
dp[i][0]=dp[i-1][0];
for(int k=1;k<=i-k&&k<=m;k++)
{
dp[i][0]=max(dp[i][0],dp[i-k][k]);
}
}
cout<<dp[n][0]<<endl;
}
return 0;
}