鞍山的dp题 hdu5074

Hatsune Miku

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 697    Accepted Submission(s): 500


Problem Description
Hatsune Miku is a popular virtual singer. It is very popular in both Japan and China. Basically it is a computer software that allows you to compose a song on your own using the vocal package.

Today you want to compose a song, which is just a sequence of notes. There are only m different notes provided in the package. And you want to make a song with n notes.


Also, you know that there is a system to evaluate the beautifulness of a song. For each two consecutive notes a and b, if b comes after a, then the beautifulness for these two notes is evaluated as score(a, b).

So the total beautifulness for a song consisting of notes a 1, a 2, . . . , a n, is simply the sum of score(a i, a i+1) for 1 ≤ i ≤ n - 1.

Now, you find that at some positions, the notes have to be some specific ones, but at other positions you can decide what notes to use. You want to maximize your song’s beautifulness. What is the maximum beautifulness you can achieve?
 

Input
The first line contains an integer T (T ≤ 10), denoting the number of the test cases.

For each test case, the first line contains two integers n(1 ≤ n ≤ 100) and m(1 ≤ m ≤ 50) as mentioned above. Then m lines follow, each of them consisting of m space-separated integers, the j-th integer in the i-th line for score(i, j)( 0 ≤ score(i, j) ≤ 100). The next line contains n integers, a 1, a 2, . . . , a n (-1 ≤ a i ≤ m, a i ≠ 0), where positive integers stand for the notes you cannot change, while negative integers are what you can replace with arbitrary notes. The notes are named from 1 to m.
 

Output
For each test case, output the answer in one line.
 

Sample Input
  
  
2 5 3 83 86 77 15 93 35 86 92 49 3 3 3 1 2 10 5 36 11 68 67 29 82 30 62 23 67 35 29 2 22 58 69 67 93 56 11 42 29 73 21 19 -1 -1 5 -1 4 -1 -1 -1 4 -1
 

Sample Output
  
  
270 625
 

Source
 

Recommend
liuyiding
 
本周做了鞍山的题目,主要是因为到机房熟悉系统,使用gvim花了好长时间,毕竟之前太懒了都没有怎么尝试过不是win的系统,虽然后来用起来很爽。。唔,就是用户体验很不错啦,colo eTab简直帅气。。只是不能跳出窗口,只能:make来看看代码是否有问题。可能是我太菜了吧,汗。。。我连:make当时都搞不清。。。汗。。。各种光标的操作,代码的复制都慢慢学,比如不在vim里面复制粘贴,直接用别的方式打开来复制。。。想来大概是因为我太菜了哈哈
对了,好像是通神给的光标操作的练习游戏,vim adventures,搜索下就出来网站了, http://vim-adventures.com/有空可以去试试,我都快忘了,,果然人老了记性差了哈哈哈



恩,来说这道dp题目。题意是给你两两音符的score,然后给你一个未完成的乐谱,用数组表示,如果元素是-1就是可以选择任意音符,如果元素>0则这个位置只能用这个音符。要使得从开始到结束每对前后音符的score和最大,求这个值。
dp我还不怎么熟悉,所以这也是个好机会,而且这个是比较浅的dp题目,比较适合我这种想要捡回算法的人吧。
这个分四种情况。我们采用顺序来推,而一个点是要计算两次score的,所以要考虑的是当前点和前面那个点的情况。
设dp[i][j]代表当前是第i个位置,j代表这个位置上的音符是哪个。s[a][b]代表a、b能得到的score是多少,b[i]为未完成乐谱的输入储存处。
则:
若b[i]>0&&b[i-1]>0,那么毫无争议,前后的音符都固定了。那么dp[i][j]就是dp[i][b[i]],=dp[i-1][b[i-1]]+s[b[i-1]][b[i]];
若b[i]<0&&b[i-1]>0,则在前面已确定的情况下选出一个音符使得s[b[i-1]][j]最大。0<j<m。dp[i][j]=max(dp[i][j1],dp[i-1][b[i-1]]+s[b[i-1]][j]);
若b[i]>0&&b[i-1]<0,则为当前已确定,前一个不确定。所以dp[i][j]=max(dp[i][j],dp[i-1][j]+s[j][b[i]]);
最后就是两个都不确定的情况,则两次筛选。dp[i][j]=max(dp[i][j],dp[i-1][k]+s[k][j]);0<k<m。
代码之后再贴,当时实现好像出问题了。今天太迟了,要睡觉,不然对身体不好。毕竟要是搞出来代码还是要一定时间而不是一两分钟的,所以现在洗洗睡了。
おやすみ
================================第二天============web课竟然老师生病不上了所以来搞这个=======================================
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int n,m;
int s[52][52];
int dp[102][52];//dp[i][j]表示为前i个音符,以第j种音符结尾所获得的最大分数
int b[102];
int ans;

void DP()
{
  memset(dp,0,sizeof(dp));
  for(int i=2;i<=n;i++)
  {
    if(b[i]>0)
    {
      if(b[i-1]>0)
        dp[i][b[i]]=dp[i-1][b[i-1]]+s[b[i-1]][b[i]];
      else
      {
        for(int j=1;j<=m;j++)
          dp[i][b[i]]=max(dp[i][b[i]],dp[i-1][j]+s[j][b[i]]);
      }
    }
    else
    {
      if(b[i-1]>0)
      {
        for(int j=1;j<=m;j++)
          dp[i][j]=max(dp[i][j],dp[i-1][b[i-1]]+s[b[i-1]][j]);
      }
      else
      {
        for(int j=1;j<=m;j++)
          for(int k=1;k<=m;k++)
          dp[i][j]=max(dp[i][j],dp[i-1][k]+s[k][j]);
      }
    }
  }
  ans=-1;
  if(b[n]>0)
    ans=dp[n][b[n]];
  else
  {
    for(int j=1;j<=m;j++)
    {
      if(ans<dp[n][j])
        ans=dp[n][j];
    }
  }
}

int main()
{
  int t;
  cin>>t;
  while(t--)
  {
    cin>>n>>m;
    for(int i=1;i<=m;i++)
      for(int j=1;j<=m;j++)
      cin>>s[i][j];
    for(int i=1;i<=n;i++)
      cin>>b[i];
    DP();
    cout<<ans<<endl;
  }
  return 0;
}
自己当时代码只做到了当前这一步,还没有搞定,然后时间不够了。现在来继续。下面是残废的代码(建议跳过):
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int m,n;
		cin>>m>>n;
		int s[110][110],b[110],dp[110][110];
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<m;j++)
			cin>>s[i][j];
		}
		for(int i=0;i<n;i++)
		{
			cin>>b[i];
		}
		for(int i=1;i<m;i++)
		{
			if(b[i]>0)
			{
				if(b[i-1]>0)
				{
					dp[i][b[i]]=dp[i-1][b[i-1]]+s[b[i-1]][b[i]];
					if(i==m)
						cout<<dp[i][b[i]]<<endl;
				}
				else
				{
					for(int j=0;j<m;j++)
						dp[i][b[i]]=max(dp[i][b[i]],dp[i-1][j]+s[j][b[i]]);
				}
			}
			else if(b[i-1]>0)
				for(int j=0;j<m;j++)
				dp[i][j]=max(dp[i][j],dp[i-1][b[i-1]]+s[b[i-1]][j]);
			else 
				for(int j=0;j<m;j++)
					for(int k=0;k<m;k++)
					{
						dp[i][j]=max(dp[i][j],dp[i-1][k]+s[k][j]);
					}
			if(i==m&&b[i]>0)
			  {int ans=0;
			    for(int j=1;j<=m;j++)
			    {
      				if(ans<dp[n][j])
			        ans=dp[n][j];
			    }
			  }
			cout<<ans<<endl;

			
		}
	}
	return 0;
}

然后我盯着四个等式思考了一会儿,觉得有问题。dp[i][j]决定的是到当前这一步最高分,但是分数是需要dp[i-1][x]  dp[i][y]这里面的x和y共同决定的,那么要取最高的分数很有可能要改变x和y两个数。但是改了x之后x和x前面的不就对不上号了吗?那就不是最高的score了啊。。。
这里我的错误是我把dp[i][j]当成了单唯的了,就是d[i-1][j]我以为只保存当前score最高的那种情况,而忘记了不同的j的取值,dp都记录着的,所以我不是在前面那个x确定(无论是我们选择的最优解还是题目给出的固定的)的前提下选择当前最优的y,而是在前面每个可能x所带来的积累的score的情况下,选出对于每个x而言最好的y,然后把所有的x和最优y组合后的score对比选出最好的。这样的话其实是能保证得出的解是最优的。(たぶんそうです)

于是滚去处理代码细节。

中间有些细节还不够完美所以调整了好几次才运行成功,怎么说呢,每次点运行的心理活动就像买彩票,明知基本上是有问题的还是想试试,万一中奖了呢?
同时。。。还有个狗血的事,就是我兴冲冲交了结果发现wa了。。。心中奔腾的草泥马啊。。于是我继续看啊看啊看,然后再交了,又wa。。。我。。。。。然后我发现,我忘记把我用来测试的一个cout语句给删掉了········泪目,删了就ac了。。。
下面是我自己的ac代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int main()
{
	int t;
	cin>>t;//t种情况 
	while(t--)
	{
		int m,n;
		cin>>n>>m;//m个音符,乐谱长n 
		int s[110][110],b[110],dp[110][110],ans=0;//s是两两音符之间的score,b是乐谱,dp是记录结果,ans是输出结果 
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=m;i++)
		{
			for(int j=1;j<=m;j++)
			cin>>s[i][j];
		}
		for(int i=0;i<n;i++)
		{
			cin>>b[i];
		}
		for(int i=1;i<n;i++)
		{
			if(b[i]>0)
			{
				if(b[i-1]>0)
				{
					dp[i][b[i]]=dp[i-1][b[i-1]]+s[b[i-1]][b[i]];
				}
				else
				{
					for(int j=1;j<=m;j++)
						dp[i][b[i]]=max(dp[i][b[i]],dp[i-1][j]+s[j][b[i]]);
				}
					if(i==n-1)//输出ans 
						cout<<dp[i][b[i]]<<endl;
			}
			else
			{
			 	if(b[i-1]>0)
					for(int j=1;j<=m;j++)
					{
						dp[i][j]=max(dp[i][j],dp[i-1][b[i-1]]+s[b[i-1]][j]);
					} 
				else 
					for(int j=1;j<=m;j++)
						for(int k=1;k<=m;k++)
						{
							dp[i][j]=max(dp[i][j],dp[i-1][k]+s[k][j]);
						}
				if(i==n-1)
 		 		{
			   		 for(int j=1;j<=m;j++)
		   			 {
   						if(ans<dp[i][j])
   			 			ans=dp[i][j];
		   			 }
					cout<<ans<<endl;
	 			}
			  } 
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值