@Metralix
2018-03-08T22:33:28.000000Z
字数 3989
阅读 862
最短路
题目大意
有三个杯子它们的容量分别是a,b,c, 并且初始状态下第一个和第二个是空的, 第三个杯子是满水的。可以把一个杯子的水倒入另一个杯子,当然,当被倒的杯子满了或者倒的杯子水完了,就不能继续倒了。
你的任务是写一个程序计算出用最少的倒水量,使得其中一个杯子里有d升水。如果不能倒出d升水的话,那么找到一个d' < d ,使得d' 最接近d。
解题思路
因为共有3个水杯, 根据每一杯的水量v1,v2,v3, 可以得到一个状态state(v1,v2,v3);
为了方便进行dfs搜索的状态转移,可以用两个三维数组volume[3], state[3]分别表示三个杯子的容量和状态。然后会有倒水的动作,可以从第1个杯子倒入第2,3个,从第2个倒入第1,3个等等……所以用两个for循环,可以遍历出所有的倒水方案。
然后注意一些不能倒水的条件,比如要倒的杯子是空的,目标杯子是满的,则不进行倒水的动作。
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
#define ll long long
#define NMAX 20000
typedef int state[3];
state st[NMAX],a;
int pour[NMAX];
int record[205];
int target;
int vis[205][205],vispour[205][205];
int try_to_insert(int x,int tpour)
{
state &k = st[x];
if(vis[k[0]][k[1]] != 1 || vispour[k[0]][k[1]] > tpour)
{
vis[k[0]][k[1]] = 1;
vispour[k[0]][k[1]] = tpour;
return 1;
}
return 0;
}
int main()
{
// freopen("input.txt","r",stdin);
// freopen("o.txt","w",stdout);
int i,j,t,ans;
scanf("%d",&t);
while(t--)
{
memset(record,0,sizeof(record));
memset(vis,0,sizeof(vis));
memset(vispour,0,sizeof(vis));
scanf("%d%d%d%d",&a[0],&a[1],&a[2],&target);
st[1][0] = st[1][1] = 0;
st[1][2] = a[2];
memset(pour,0,sizeof(pour));
record[a[2]] = 1;
int front = 1,rear = 2;
vis[0][0] = 1;
bool flag = false;
while(front < rear)
{
for(i = 0; i < 3; i++)
if(record[st[front][i]] == 0 || pour[record[st[front][i]]] > pour[front])
record[st[front][i]] = front;
for(i = 0; i < 3; i++)
if(st[front][i] == target)
{
if(flag)
ans = pour[ans] > pour[front]?front:ans;
else
ans = front;
flag = true;
break;
}
for(i = 0; i < 3; i++)
{
state &w = st[front];
if(w[i] != 0)
{
for(j = 0; j < 3; j++)
{
if(i == j || (i != j && w[j] == a[j])) continue;
state &temp = st[rear];
memcpy(temp,w,sizeof(w));
int pp;
if(w[i] + w[j] > a[j])
{
pp = a[j] - w[j];
temp[i] = w[i] - pp;
temp[j] = a[j];
}
else
{
pp = w[i];
temp[i] = 0;
temp[j] = w[j] + pp;
}
int tpour;
tpour = pour[front] + pp;
if(try_to_insert(rear,tpour))
{
pour[rear] = tpour;
rear++;
}
}
}
}
front++;
}
if(record[target] == 0)
{
for(i = target; record[i] == 0; i--);
printf("%d %d\n",pour[record[i]],i);
}
else printf("%d %d\n",pour[ans],target);
}
return 0;
}
标签: spfa
题目大意
有n个村庄和m个城堡,村庄编号从1开始,城堡编号从n+1开始,一个人带着公主要从城堡n+m到村庄1,他还有一个魔法鞋可以有cnt次的使用机会可以让人能不花费时间走最多l,人只能在路上没有城堡的时候使用这个魔法鞋,问最少花费多少时间能到达村庄1。
解题思路
最短路径问题,用spfa算法,先用floyd将地点i到j的最短距离求出(不经过城堡),然后用f[i][j]表示到达结点i还剩j次穿魔法鞋机会的花费时间,与普通的spfa不同的是加了一个判断:f[u][t1] > f[i][t1-1],意味着从u到i用一次魔法鞋如果可以减短时间就用掉,并且如果不再队列中就加到队尾。
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<stack>
#include<iostream>
#include<list>
#include<set>
#include<cmath>
#define INF 0x7fffffff
#define eps 1e-6
#define LL long long
using namespace std;
int a,b,m,l,k,n;
struct lx
{
int v,len;
};
vector<lx>g[101];
int dis[101][101];
void SPFA(int start)
{
queue<int>q;
bool vis[101];
memset(vis,0,sizeof(vis));
q.push(start);
vis[start]=1;
dis[start][start]=0;
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=0;
for(int j=0;j<g[u].size();j++)
{
int v=g[u][j].v;
int len=g[u][j].len;
if(dis[start][v]>dis[start][u]+len)
{
dis[start][v]=dis[start][u]+len;
if(v<=a&&!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
void spfa()
{
bool vis[101];
int dp[101][11];
for(int i=0;i<101;i++)
{
vis[i]=0;
for(int j=0;j<11;j++)
dp[i][j]=INF;
}
queue<int>q;
q.push(n);
vis[n]=1;
dp[n][k]=0;
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=0;
for(int i=0;i<=k;i++)
{
for(int j=0;j<g[u].size();j++)
{
int v=g[u][j].v;
int len=g[u][j].len;
if(dp[u][i]!=INF&&dp[v][i]>dp[u][i]+len)
{
dp[v][i]=dp[u][i]+len;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
// printf("%d > %d+%d\n",dp[v][i],dp[u][i],len);
// dp[u][i]!=INF;注意
}
if(i==0)continue;
for(v=1;v<=n;v++)
{
if(v!=u&&dis[u][v]<=l)
{
if(dp[v][i-1]>dp[u][i])
{
dp[v][i-1]=dp[u][i];
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
}
}
int ans=INF;
for(int i=0;i<=k;i++)
ans=min(ans,dp[1][i]);
printf("%d\n",ans);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d%d",&a,&b,&m,&l,&k);
for(int i=0;i<101;i++)
g[i].clear();
n=a+b;
int u,v,len;
while(m--)
{
scanf("%d%d%d",&u,&v,&len);
lx now;
now.len=len;
now.v=v,g[u].push_back(now);
now.v=u,g[v].push_back(now);
}
for(int i=0;i<101;i++)
for(int j=0;j<101;j++)
dis[i][j]=INF;
for(int i=1;i<=n;i++)
SPFA(i);
spfa();
}
}