timus 1244. Gentlemen URAL 解题报告
用所给的数能否把一个容量为val的背包装满,如果能装满的话,记录有几种方式,如果只有一种方式的话输出没用到的“牌”的下标!(和路径回溯有点相似)
满背包思想就好,唯一的一点有难度的地方就是在处理路径的时候,path[j]记录的是容量j是由前一个容量加上哪一个下标的重量来的,回溯的时候利用dp[j- num[path[j]] ] 回溯到上一个容量,再由上一个容量找上一个回溯点,并且把下标记录,放到一个栈里面,其实不用放也行,然后对于路径中的下标在vis[i]数组中置访问过的标志,然后将没用用过的下标输出来就好! 路径回溯的时候稍稍有点绕!但是仔细一想还行能明白的,只不过不熟练的话花的时间会有点多的!
这个题没有这种类型的测试数据:
2
2
1 1 这样的话什么也不熟出。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<set>
#include<vector>
#include<list>
#include<stack>
#include<queue>
using namespace std;
#define mem(a,v) memset(a,v,sizeof(a))
#define N (1000*100)
typedef pair<int,int> pp;
typedef long long ll;
int dp[N+10];
int val,n;
int num[110];
int path[N+10];
int ord[N+10];
int main()
{
//freopen("data.in","r",stdin);
while(scanf("%d %d",&val,&n)!=EOF)
{
mem(num,0);
mem(dp,0);
mem(path,0);
mem(ord,0);
for(int i=1;i<=n;++i)
{
scanf("%d",&num[i]);
ord[num[i]]=i;
//dp[num[i]]++;
}
dp[0]=1;
for(int i=1;i<=n;++i)
{
for(int j=N;j>=num[i];j--)
{
if(dp[j]==0&&dp[j-num[i]])
{
dp[j]=dp[j-num[i]];
path[j]=i;///只在这里记录路径就行,因为只有路径是1的才可能回溯
}else if(dp[j]&&dp[j-num[i]])
{
dp[j]+=dp[j-num[i]];
}
}
}
// cout<<"====="<<dp[2]<<endl;
if(dp[val]==0)
{
cout<<0<<endl;
}else if(dp[val]>1)
{
cout<<-1<<endl;
}else
{
int vis[n+5];
mem(vis,0);
stack<int> sta;
int ind=path[val];
int tmp=val;
while(tmp!=0)
{ //cout<<"ind"<<ind<<endl;
sta.push(ind);
tmp-=num[ind];
ind=path[tmp];
}
while(!sta.empty())
{
vis[sta.top()]=1;///表示该下标已经访问过
sta.pop();
}
for(int i=1;i<=n;++i)
{
if(vis[i]==0)cout<<i<<' ';
}cout<<endl;//cout<<"okok"<<endl;
}
}
return 0;
}