原题:
Your company provides robots that can be used to pick up litter from fields after sporting events and concerts. Before robots are assigned to a job, an aerial photograph of the field is marked with a grid. Each location in the grid that contains garbage is marked. All robots begin in the Northwest corner and end their movement in the Southeast corner. A robot can only move in two directions, either to
the East or South. Upon entering a cell that contains garbage, the robot can be programmed to pick it up before proceeding. Once a robot reaches its destination at the Southeast corner it cannot be repositioned or reused. Since your expenses are directly proportional to the number of robots used for a particular job, you are interested in making the most out of them. Your task would be to use a robot
to clean the maximum number of cells containing garbage. Now there can be many ways to do this job, so your task would be to report that number of ways and show us one such sample. You see your robot can traverse many cells without picking up garbage, so for us a valid solution would be the sequence of cell numbers that the robot cleans. The robots only clean cells that contain garbage; but you can program them to avoid picking up garbage from specific cells, if you would want to.
In the figure above we show a field map that has 6 rows and 7 columns. The cells in a field map are numbered in row major order starting from 1. For the example shown here, the following 7 cells contain garbage: 2 (1,2), 4 (1,4), 11 (2, 4), 13 (2, 6), 25 (4, 4), 28 (4, 7) and 41 (6, 7). Here cells are presented in cell number (row, column) format. Now the maximum number of cells that can be cleaned is 5, and there are f different ways to do that:
< 2,4,11,13,28 >
< 2,4,11,13,41 >
< 2,4,11,25,28 >
< 2,4,11,25,41 >
Input
An input file consists of one or more field maps followed by a line containing ‘-1 -1’ to signal the end of the input data. The description of a field map starts with the number of rows and the number of columns in the grid. Then in the subsequent lines, the garbage locations follows. The end of a field map is signaled by ‘0 0’. Each garbage location consists of two integers, the row and column, separated by
a single space. The rows and columns are numbered as shown in Figure 1. The garbage locations will not be given in any specific order. And a location would not be reported twice for a field map. Please note that for all the test cases you are required to solve, the field map would be of at most 100 rows and 100 columns.
Output
The output for each test case starts with the serial number (starting from 1) for that test case. Then the following integers are listed on a line: N the maximum number of cells that the robot can clean, C the number of ways that these N cells can be cleaned, and N numbers describing one possible sequence of cell numbers that the robot will clean. As there can be C different such sequences and we are asking for only one sequence any valid sequence would do. Make sure that all these 2 + N integers for a test case are printed on a single line. There must be one space separating two consecutive integers and a space between the colon and the first integer on the line. See the sample output format for a clear idea.
Sample Input
6 7
1 2
1 4
2 4
2 6
4 4
4 7
6 6
0 0
4 4
1 1
2 2
3 3
4 4
0 0
-1 -1
Sample Output
CASE#1: 5 4 2 4 11 13 28
CASE#2: 4 1 1 6 11 16
中文:
给你n*m的网格,有得格子里有垃圾,现在让你派出一个机器人,去捡垃圾。机器人从(1,1)开始,走到(n,m),只能向下或者向右走。问你最多能捡到多少个垃圾,而且在捡到最多的垃圾的路径数目是多少,输出路径数目。 注意,此处的路径的节点仅仅为含有垃圾格子,见题目中的样例。
代码:
暴力搜索路径的超时代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=101;
int dp[maxn][maxn];
int mat[maxn][maxn];
int n,m;
int cnt;
unordered_set<string> vis;
void dfs(int i,int j,string path)
{
if(i==0||j==0)
return;
string tmp=path;
if(mat[i][j])
{
tmp+=to_string((i-1)*m+j)+" ";
}
if(dp[i][j]==1&&mat[i][j])
{
if(vis.find(tmp)==vis.end())
{
vis.insert(tmp);
cnt++;
return;
}
else
{
return;
}
}
else
{
if(dp[i-1][j]==dp[i][j-1])
{
dfs(i-1,j,tmp);
dfs(i,j-1,tmp);
}
else
{
if(dp[i-1][j]>dp[i][j-1])
dfs(i-1,j,tmp);
else
dfs(i,j-1,tmp);
}
}
}
int main()
{
ios::sync_with_stdio(false);
int t=1;
while(cin>>n>>m)
{
vis.clear();
if(n==-1)
break;
if(n==0||m==0)
continue;
int r,c;
memset(mat,0,sizeof(mat));
memset(dp,0,sizeof(dp));
while(cin>>r>>c,r+c)
mat[r][c]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mat[i][j])
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
cout<<"CASE#"<<t++<<": "<<dp[n][m]<<" ";
cnt=0;
dfs(n,m,"");
cout<<cnt<<" ";
string tmp=*(vis.begin());
stringstream ss;
ss<<tmp;
vector<int> v;
int res;
while(ss>>res)
{
v.push_back(res);
}
for(int i=v.size()-1;i>=0;i--)
{
if(i==0)
cout<<v[i]<<endl;
else
cout<<v[i]<<" ";
}
}
return 0;
}
使用逆邻接表的超时代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=101;
int dp[maxn][maxn];
int mat[maxn][maxn];
int n,m;
int cnt,flag;
set<int> G[10001];
vector<int> g[10001];
vector<int> path;
void dfs_path_num(int node)
{
if(node==n*m)
{
cnt++;
return;
}
for(auto x:g[node])
{
dfs_path_num(x);
}
}
void dfs_path(int node)
{
if(flag)
return;
path.push_back(node);
if(node==n*m)
{
flag=1;
return;
}
for(auto x:g[node])
{
dfs_path(x);
}
}
int main()
{
ios::sync_with_stdio(false);
int t=1;
while(cin>>n>>m)
{
if(n==-1)
break;
if(n==0||m==0)
continue;
int r,c;
cnt=0;
flag=0;
memset(mat,0,sizeof(mat));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n*m;i++)
{
G[i].clear();
g[i].clear();
}
path.clear();
int f=0,e=0;
while(cin>>r>>c,r+c)
mat[r][c]=1;
if(mat[1][1]==1)
f=1;
if(mat[n][m]==1)
e=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mat[i][j])
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
cout<<"CASE#"<<t++<<": "<<dp[n][m];
mat[1][1]=1;//起点
mat[n][m]=1;//终点
for(int j=1;j<=m;j++)
{
if(mat[1][j-1])
G[j].insert(j-1);
else
{
for(auto x:G[j-1])
G[j].insert(x);
}
}
if(mat[1][1])
{
for(int i=2;i<=n;i++)
{
int tmp=(i-1)*m+1;
G[tmp].insert(1);
}
}
for(int i=2;i<=n;i++)//建立逆邻接表
{
for(int j=2;j<=m;j++)
{
int tmp=(i-1)*m+j;
int u=(i-1-1)*m+j;
int l=(i-1)*m+j-1;
if(dp[i-1][j]==dp[i][j-1])
{
if(mat[i-1][j]&&mat[i][j-1])
{
G[tmp].insert(u);
G[tmp].insert(l);
}
if(mat[i-1][j]&&!mat[i][j-1])
{
G[tmp].insert(u);
for(auto x:G[l])
G[tmp].insert(x);
}
if(!mat[i-1][j]&&mat[i][j-1])
{
G[tmp].insert(l);
for(auto x:G[u])
G[tmp].insert(x);
}
if(!mat[i-1][j]&&!mat[i][j-1])
{
G[tmp]=G[u];
for(auto x:G[l])
G[tmp].insert(x);
}
}
else
{
if(dp[i-1][j]>dp[i][j-1])
{
if(mat[i-1][j])
G[tmp].insert(u);
else
{
for(auto x:G[u])
G[tmp].insert(x);
}
}
else
{
if(mat[i][j-1])
G[tmp].insert(l);
else
{
for(auto x:G[l])
G[tmp].insert(x);
}
}
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mat[i][j])
{
int tmp=(i-1)*m+j;
for(auto x:G[tmp])
g[x].push_back(tmp);
}
}
}
/*
for(int i=1;i<=n*m;i++)
{
if(!g[i].empty())
{
cout<<i<<"->";
for(auto x:g[i])
cout<<x<<" ";
cout<<endl;
}
}
*/
dfs_path_num(1);
cout<<" "<<cnt;
dfs_path(1);
if(f&&e)
{
for(int i=0;i<path.size();i++)
cout<<" "<<path[i];
}
else
{
if(!f&&e)
{
for(int i=1;i<path.size();i++)
cout<<" "<<path[i];
}
else
{
if(f&&!e)
{
for(int i=0;i<path.size()-1;i++)
cout<<" "<<path[i];
}
else
{
for(int i=1;i<path.size()-1;i++)
cout<<" "<<path[i];
}
}
}
cout<<endl;
}
return 0;
}
看着别人博客写过的代码~_~!!
博客链接https://www.cnblogs.com/staginner/archive/2011/12/06/2278453.html
#include<bits/stdc++.h>
using namespace std;
const int maxn=101;
const int maxN=maxn*maxn;
int dp[maxN];
int mat[maxn][maxn];
int seq[maxN],y[maxN],d[maxN],num[maxN],p[maxN];
int n,m;
int cnt,flag;
void dfs_path(int x)
{
if(p[x]!=-1)
dfs_path(p[x]);
for(int i=0;i<d[x];i++)
cout<<" "<<seq[x];
}
int main()
{
ios::sync_with_stdio(false);
int t=1;
while(cin>>n>>m)
{
if(n==-1)
break;
if(n==0||m==0)
continue;
int r,c;
memset(mat,0,sizeof(mat));
memset(dp,0,sizeof(dp));
memset(p,-1,sizeof(p));
int f=0,e=0;
int ind=0;
while(cin>>r>>c,r+c)
mat[r][c]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mat[i][j])
{
seq[ind]=(i-1)*m+j;
y[ind]=j;
d[ind]=1;
ind++;
}
}
}
if(seq[ind-1]!=n*m)
{
seq[ind]=n*m;
y[ind]=m;
d[ind]=0;
ind++;
}
fill(num,num+ind,1);
for(int i=0;i<ind;i++)
{
for(int j=0;j<i;j++)
{
if(y[j]<=y[i])//列数满足
{
if(dp[j]>dp[i])//f中存储最创递增子序列
{
dp[i]=dp[j];
num[i]=num[j];//存储路径数目
p[i]=j;//存储上一个节点,用来输出路径
}
else
{
if(dp[j]==dp[i])//如果路径长度相等,累加路径是
num[i]+=num[j];
}
}
}
dp[i]+=d[i];
}
cout<<"CASE#"<<t++<<": "<<dp[ind-1]<<" "<<num[ind-1];
dfs_path(ind-1);
cout<<endl;
}
return 0;
}
解答:
该题目一共有3个问,第一个问相当简单,使用二维动态规划直接解出。
状态转移方程为dp[i][j]记录从起点到(i,j)最多捡垃圾的个数,mat[i][j]标记是否有垃圾
如果mat[i][j]有垃圾
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+1;
否则
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
第二个求解最长值的路径数就有点邪乎了
大概看了下数据量,感觉会超时,决定实践一番,直接使用暴力搜索,使用set记录是否重复,最后set的大小就是路径的个数。
果不其然,超时~~ -_-!
接下来想其他的办法,最容易想到的就是把有垃圾的格子建立一个图,首先要对所有(i,j)建立一个图,实际上时一个二维的哈希表,里面存储着有哪个含有垃圾的格子的能够到达(i,j)点的编号,得到的是一个逆邻接表。取出逆邻接表中只含有垃圾的(i,j)节点,建立图,然后从起点寻找路径数。 考虑极端情况,有100×100的格子,每个格子都有垃圾,那么….. 每个格子要保留的编号数目的平均值为5000个,如果有这种数据,时间复杂度肯定爆炸。
本人依然抱着侥幸的心态试了一试,果然超时-_-~~
如果此题这么简单就不会只有2000多人提交,400多人过了。
看了一下别人的博客,几乎都是将问题转换为最长递增子序列问题。由于,机器人只能向右、向下走。所以,将每个垃圾节点的编号保留在一个序列seq当中,同时记录每个节点的编号对应的列数,在使用最长递增子序列的算法时,限制列数条件要求即可。
最长递增子序列问题求路径和方案数就方便多了。。。