+ Нов въпрос
vesela.k 1 Точки

## P!rates изпитна задача Fundamentals Final Exam

Здравейте, хора!

Решавам я тая задачка, но на втория zero test на последния вход ми чупи кода и изобщо нямам идея защо. Много бих благодарна, ако някой има време да я погледне и да ме просветли в живота!

Това е задачата:

``````Description
Until the "Sail" command is given you will be receiving:
Cities that you and your crew have targeted, with their population and gold, separated by "||".
If you receive a city which has been already received, you have to increase the population and gold with the given values.
After the "Sail" command, you will start receiving lines of text representing events until the "End" command is given.
Events will be in the following format:
"Plunder=>{town}=>{people}=>{gold}"
You have successfully attacked and plundered the town, killing the given number of people and stealing the respective amount of gold.
For every town you attack print this message: "{town} plundered! {gold} gold stolen, {people} citizens killed."
If any of those two values (population or gold) reaches zero, the town is disbanded.
You need to remove it from your collection of targeted cities and print the following message: "{town} has been wiped off the map!"
There will be no case of receiving more people or gold than there is in the city.
"Prosper=>{town}=>{gold}"
There has been a dramatic economic growth in the given city, increasing its treasury by the given amount of gold.
The gold amount can be a negative number, so be careful. If a negative amount of gold is given print: "Gold added cannot be a negative number!" and ignore the command.
If the given gold is a valid amount, increase the town's gold reserves by the respective amount and print the following message: "{gold added} gold added to the city treasury. {town} now has {total gold} gold."

Input
On the first lines, until the "Sail" command, you will be receiving strings representing the cities with their gold and population, separated by "||"
On the next lines, until the "End" command, you will be receiving strings representing the actions described above, separated by "=>"
Output
After receiving the "End" command if there are any existing settlements on your list of targets, you need to print all of them, sorted by their gold in descending order, then by their name in ascending order, in the following format:
Ahoy, Captain! There are {count} wealthy settlements to go to:
{town1} -> Population: {people} citizens, Gold: {gold} kg
…
{town…n} -> Population: {people} citizens, Gold: {gold} kg
If there are no settlements left to plunder, print:
"Ahoy, Captain! All targets have been plundered and destroyed!"

INPUT
Nassau||95000||1000
San Juan||930000||1250
Campeche||270000||690
Port Royal||320000||1000
Port Royal||100000||2000
Sail
Prosper=>Port Royal=>-200
Plunder=>Nassau=>94000=>750
Plunder=>Nassau=>1000=>150
Plunder=>Campeche=>150000=>690
End

OUTPUT
Gold added cannot be a negative number!
Nassau plundered! 750 gold stolen, 94000 citizens killed.
Nassau plundered! 150 gold stolen, 1000 citizens killed.
Nassau has been wiped off the map!
Campeche plundered! 690 gold stolen, 150000 citizens killed.
Campeche has been wiped off the map!
Ahoy, Captain! There are 2 wealthy settlements to go to:
Port Royal -> Population: 420000 citizens, Gold: 3000 kg
San Juan -> Population: 930000 citizens, Gold: 1250 kg``````

Това е моят код:

``````import java.util.*;

public class Pirates {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

List<City> targetedCities = new ArrayList<>();

String input = scanner.nextLine();
while (!input.equals("Sail")) {
String[] targetedCity = input.split("\\|\\|");

String name = targetedCity[0];
int population = Integer.parseInt(targetedCity[1]);
int gold = Integer.parseInt(targetedCity[2]);

City city = new City(name, population, gold);
if (!targetedCities.contains(city)) {
} else {
city.setPopulation(city.getPopulation() + population);
city.setGold(city.getGold() + gold);
}
input = scanner.nextLine();
}

input = scanner.nextLine();
while (!input.equals("End")) {
String[] events = input.split("=>");
String town = events[1];

switch (events[0]) {
case "Plunder":
int people = Integer.parseInt(events[2]);
int gold = Integer.parseInt(events[3]);
for (City c : targetedCities) {
if (c.getName().equals(town)) {

int afterAttackPeople = c.getPopulation() - people;
int afterAttackGold = c.getGold() - gold;

if (afterAttackGold <= 0 || afterAttackPeople <= 0) {
System.out.printf("%s plundered! %d gold stolen, %d citizens killed.\n", town, gold, people);
System.out.printf("%s has been wiped off the map!\n", town);
targetedCities.remove(c);

} else {
c.setGold(afterAttackGold);
c.setPopulation(afterAttackPeople);
System.out.printf("%s plundered! %d gold stolen, %d citizens killed.\n", town, gold, people);
}
}
break;
}
break;
case "Prosper":
gold = Integer.parseInt(events[2]);
if (gold > 0) {
for (City c : targetedCities) {
if (c.getName().equals(town)) {
c.setGold(c.getGold() + gold);
System.out.printf("%d gold added to the city treasury. %s now has %d gold.\n", gold, town, c.getGold());
}
}
} else {
System.out.println("Gold added cannot be a negative number!");

}
break;
}
input = scanner.nextLine();
}
if (targetedCities.size() > 0){
System.out.printf("Ahoy, Captain! There are %d wealthy settlements to go to:\n", targetedCities.size());
targetedCities
.stream()
.sorted(Comparator.comparing(City::getGold)
.reversed()
.thenComparing(City::getName))
.forEach(city -> System.out.printf("%s -> Population: %d citizens, Gold: %d kg\n", city.getName(), city.getPopulation(), city.getGold()));
} else {
System.out.println("Ahoy, Captain! All targets have been plundered and destroyed!");
}

}

static class City {
String name;
int population;
int gold;

public City(String name, int population, int gold) {
this.name = name;
this.population = population;
this.gold = gold;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPopulation() {
return population;
}

public void setPopulation(int population) {
this.population = population;
}

public int getGold() {
return gold;
}

public void setGold(int gold) {
this.gold = gold;
}
}
}
``````

Тагове:
1
MartinBG 3977 Точки

Има няколко проблема в решението:

``````        List<City> targetedCities = new ArrayList<>();

String input = scanner.nextLine();
while (!input.equals("Sail")) {
String[] targetedCity = input.split("\\|\\|");

String name = targetedCity[0];
int population = Integer.parseInt(targetedCity[1]);
int gold = Integer.parseInt(targetedCity[2]);

City city = new City(name, population, gold);
if (!targetedCities.contains(city)) { // Problem #1
} else { // Problem #2
city.setPopulation(city.getPopulation() + population);
city.setGold(city.getGold() + gold);
}
input = scanner.nextLine();
}``````

1. Винаги ще се влиза в if-a, защото City класа използва дефолтната имплементация на equals() метода, която връща true само ако се извика върху един и същи обект (т.е. city.equals(city)).
Това може да се оправи като презапишем метода, например:
``````    static class City {
//...

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
City city = (City) o;
return name.equals(city.name);
}
}``````

2. Тази част от условието не е спазена:

If you receive a city which has been already received, you have to increase the population and gold with the given values.

Всъщност, в else-a се модифицира отново променливата, която създаваме преди това и тя не се запаза никъде.
Може да го оправим така:
``````            } else {
city = targetedCities.stream()
.filter(c -> c.getName().equals(name))
.findFirst().orElse(city);
// ...
}``````

Следващите проблеми са при изпълнението на командата  "Plunder":

``````                case "Plunder":
int people = Integer.parseInt(events[2]);
int gold = Integer.parseInt(events[3]);
for (City c : targetedCities) {
if (c.getName().equals(town)) {

int afterAttackPeople = c.getPopulation() - people;
int afterAttackGold = c.getGold() - gold;

if (afterAttackGold <= 0 || afterAttackPeople <= 0) {
System.out.printf("%s plundered! %d gold stolen, %d citizens killed.\n", town, gold, people);
System.out.printf("%s has been wiped off the map!\n", town);
targetedCities.remove(c); // Problem 1

} else {
c.setGold(afterAttackGold);
c.setPopulation(afterAttackPeople);
System.out.printf("%s plundered! %d gold stolen, %d citizens killed.\n", town, gold, people);
}
}
break; // Problem #2
}
break;``````

1. Опитваме се да трием елемент от targetedCities докато до обхождаме, което ще хвърли ConcurrentModificationException.
Един начин да заобиколим това е, да изнесем логиката по изтриване на невалидните градове след for цикъла, например чрез removeIf:
``````                for (City c : targetedCities) {
//...
c.setGold(afterAttackGold);
c.setPopulation(afterAttackPeople);
//...
}

targetedCities.removeIf(city -> city.getGold() <= 0 || city.getPopulation() <= 0);

break;``````

2. Този break е излишен, макар и да не чупи логиката на програмата.

Това е решението с горните промени (100/100).

Тъй като градовете са с гарантирано уникални имена по условие и защото трябва да ги достъпваме по име, по-удачен и лесен вариант за решаване на задачата е, ако пазим градовете в Map<String, City> като за ключ използваме името на града, като при това решение не се налага предефиниране на equals() и на hashCode().

1
MANEKENA 118 Точки

Ето ти и един по съкратен вариант без клас https://pastebin.com/YGL2BC8Z

1