++
弄懂代码很容易,但是问题的关键是----为什么可以这么做??----对于状态的划分和转移,最关键的一点就是----不重不漏,对于确定的终点,我们应该要怎么设计方程呢?也就是说,要如何高效地枚举所有经过节点,并到达终点的情况。
假设:一共有七个点,用0,1,2,3,4,5,6来表示,那么先假设终点就是5,在这里我们再假设还没有走到5这个点,且走到的终点是4,那么有以下六种情况:
first: 0–>1–>2–>3–>4 距离:21
second: 0–>1–>3–>2–>4 距离:23
third: 0–>2–>1–>3–>4 距离:17
fourth: 0–>2–>3–>1–>4 距离:20
fifth: 0–>3–>1–>2–>4 距离:15
sixth: 0–>3–>2–>1–>4 距离:18
同理:假设还没有走到5这个点儿,且走到的终点是3,那么有一下六种情况:
first: 0–>1–>2–>4–>3 距离:27
second: 0–>1–>4–>2–>3 距离:22
third: 0–>2–>1–>4–>3 距离:19
fourth: 0–>2–>4–>1–>3 距离:24
fifth: 0–>4–>1–>2–>3 距离:26
sixth: 0–>4–>2–>1–>3 距离:17
那么由于4-5,3-5的距离是个定值,就可以以此来确定到达5的最短距离。因此,我们不妨可以这样枚举------确定一个终点,枚举所有点到该终点的距离,并取最小值来更新答案。
.DP分析:
用二进制来表示要走的所以情况的路径,这里用i来代替
例如走0,1,2,4这三个点,则表示为:10111;
走0,2,3这三个点:1101;
#include<iostream>
#include<cstring>
using namespace std;
const int M=1<<20;
int n;
int w[21][21];
int f[M][21];
int a,b;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cin>>w[i][j];
}
}
//对状态进行枚举,接着在每个状态下枚举所有终点的情况,对于终点来说,在对应的点,枚举可以由点k迁移而来的点。
memset(f,0x3f3f3f,sizeof f);
f[1][0]=0;
for(int i=0;i<1<<n;i++)
{
for(int j=0;j<n;j++)
{
if(i>>j&1) //表示这种情况至少要包含j。
{
for(int k=0;k<n;k++)
{
if((i-(1<<j))>>k&1) //去掉j的所有路径是否包含k
{
f[i][j]=min(f[i-(1<<j)][k]+w[k][j],f[i][j]);
}
}
}
}
}
cout<<f[(1<<n)-1][n-1];
return 0;
}
(2)
本题采用优化枚举,用空间换时间,从后往前遍历,依次记录当前位置的字符对应的下一个(a——z)在什么位置,没有的话就置为-1,紧接着查询即可。
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e6+5;
char a[maxn],b[maxn];
int t,last[30],nxt[maxn][30];
void init(){
memset(last,-1,sizeof last);
int d=strlen(a+1);
for(int i=d;i>=1;i--)
{
for(int j=1;j<=26;j++)
nxt[i][j]=last[j];
last[a[i]-'a'+1]=i;
}
}
bool check(){
int la=strlen(a+1);
int lb=strlen(b+1);
int i=last[b[1]-'a'+1];
if(i==-1) return 0;
for(int j=2;j<=lb;j++)
{
i=nxt[i][b[j]-'a'+1];
if(i==-1) return 0;
}
return 1;
}
int main(){
scanf("%s",a+1);
init();
scanf("%d",&t);
while(t--){
scanf("%s",b+1);
if(check()){
printf("Yes\n");
}else{
printf("No\n");
}
}
return 0;
}